Should I add type annotation to keyword arguments?

Old versions of Julia’s performance tips
https://docs.julialang.org/en/v1/manual/performance-tips
advised annotating the types of keyword arguments, but this is no longer mentioned in the current version. Do I still need to pay attention to keyword arguments in terms of performance, e.g function call overhead and compiler specialization?

3 Likes

As a rule of thumb, Julia performance is similar to if all the types of the arguments are known. Not specializing is IMHO just an optimization.

I guess knowing the type might make a difference in certain cases, although whether this matters in real code is less clear.

julia> using BenchmarkTools

julia> d = Any[1]
1-element Vector{Any}:
 1

julia> f(; d, e = d[1]) = e + 1
f (generic function with 1 method)

julia> @btime f(; d = $d);
  22.575 ns (0 allocations: 0 bytes)

julia> g(; d, e::Int = d[1]) = e + 1
g (generic function with 1 method)

julia> @btime g(; d = $d);
  6.634 ns (0 allocations: 0 bytes)

I would refrain from type-annotations unless benchmarks suggested otherwise.

2 Likes

Yeah… there are some weird cases.

By the way does anyone have a source for the folk tale “Julia specializes on kwargs but does not dispatch on them”? I think I read it from @oxinabox but the trail runs cold after that

I guess it’s fairly easy to demonstrate in practice but I couldn’t find it in the official docs

julia> g(; x) = zero(x)
g (generic function with 1 method)

julia> @code_warntype g(; x=1)
MethodInstance for Core.kwcall(::NamedTuple{(:x,), Tuple{Int64}}, ::typeof(g))
  from kwcall(::Any, ::typeof(g)) @ Main REPL[9]:1
Arguments
  _::Core.Const(Core.kwcall)
  @_2::NamedTuple{(:x,), Tuple{Int64}}
  @_3::Core.Const(g)
Locals
  @_4::Int64
  x::Int64
Body::Int64  # correct inference
...

It is documented in Methods · The Julia Language, but it’s one paragraph in the middle of a pretty long page. Maybe some emphasis or hoisting of the admonition to a more prominent location would help?

2 Likes

Yeah I had that part in mind:

Keyword arguments behave quite differently from ordinary positional arguments. In particular, they do not participate in method dispatch. Methods are dispatched based only on positional arguments, with keyword arguments processed after the matching method is identified.

I was actually wondering where it says that methods are still specialized on kwargs, even though they play no part in dispatch

1 Like

I see. In a sense it’s covered by omission in Performance Tips · The Julia Language. Perhaps the follow-up question is why specialization is so often thought to be near 1-1 with dispatch. Maybe it isn’t and I’m overgeneralizing from a small sample size!

1 Like

Its only notable because Julia never used to specialize on kwargs.

1 Like

Now that I did not know. Is there some background on why and/or when the switch was made? It seems like people would’ve avoided kwargs like the plague if they weren’t specialized because of dynamic dispatch, or was that not a problem?

1 Like

It isn’t in HISTORY.md, I guess considered an implementation detail or just forgotten

I know it was after the original MLDataUtils, MLDataPattern etc was written (which was 2017ish) as IIRC that constrains lots of keyword arguments for convenience but positional for performance. (It also massively overused types because no constant folding)

@ChrisRackauckas might know as DiffEqverse was a big winner in that one.

1 Like