# Force specialization on kwargs

If I have a function like this:

``````f_vararg(varargs::Int...) = tuple(varargs...)
``````

I can force specialization on varargs with either of the following:

``````g_vararg(varargs::Vararg{Int, N}) where {N} = tuple(varargs...)
``````
``````h_vararg(varargs::Vararg{<:Any, N}) where {N} = tuple(varargs...)
``````

Now suppose instead that I have a function like this:

``````f_kwarg(; kwargs...) = (; kwargs...)
``````

How can I force specialization on the keyword arguments?

How do you check specialization of functions with keyword arguments? Does this show that apparently they are automatically specialized?

``````julia> f_kwarg(; kwargs...) = (; kwargs...)
f_kwarg (generic function with 1 method)

julia> (@which f_kwarg(a=1, b=2.0, c=:three)).specializations

julia> @code_lowered f_kwarg(a=1, b=2.0, c=:three)
CodeInfo(
1 â”€      kwargs... = Base.pairs(@_2)
â”‚   %2 = Main.:(var"#f_kwarg#3")(kwargs..., @_3)
â””â”€â”€      return %2
)

julia> (@which var"#f_kwarg#3"((a=1, b=2.0, c=:three), f_kwarg)).specializations

julia> f_kwarg(a=1, b=2.0, c=:three)
(a = 1, b = 2.0, c = :three)

julia> (@which f_kwarg(a=1, b=2.0, c=:three)).specializations
Core.TypeMapEntry(nothing, Tuple{var"#f_kwarg##kw",NamedTuple{(:a, :b, :c),Tuple{Int64,Float64,Symbol}},typeof(f_kwarg)}, nothing, svec(), 0x0000000000000001, 0xffffffffffffffff, MethodInstance for (::var"#f_kwarg##kw")(::NamedTuple{(:a, :b, :c),Tuple{Int64,Float64,Symbol}}, ::typeof(f_kwarg)), true, true, false)

julia> (@which var"#f_kwarg#3"((a=1, b=2.0, c=:three), f_kwarg)).specializations
Core.TypeMapEntry(nothing, Tuple{var"##f_kwarg#3",Base.Iterators.Pairs{Symbol,Any,Tuple{Symbol,Symbol,Symbol},NamedTuple{(:a, :b, :c),Tuple{Int64,Float64,Symbol}}},typeof(f_kwarg)}, nothing, svec(), 0x0000000000000001, 0xffffffffffffffff, MethodInstance for #f_kwarg#3(::Base.Iterators.Pairs{Symbol,Any,Tuple{Symbol,Symbol,Symbol},NamedTuple{(:a, :b, :c),Tuple{Int64,Float64,Symbol}}}, ::typeof(f_kwarg)), true, true, false)
``````

Julia does not [yet] specialize dispatch on keyword arguments.

I donâ€™t think that we can trust the output of `@code_lowered`, etc when it comes to specialization. See e.g. https://github.com/JuliaLang/julia/issues/32834 and https://github.com/JuliaLang/julia/blob/master/doc/src/manual/performance-tips.md:

Note that `@code_typed` and friends will always show you specialized code, even if Julia would not normally specialize that method call.

1 Like

Well, dispatch is a different question entirely. Keyword arguments do not participate in dispatch. But Iâ€™m asking if I can get Julia to compile specialized code for the types of the keyword arguments.

If I understand correctly, specialization on keyword arguments would not require keyword arguments to participate in dispatch.

1 Like

Iâ€™m using `@code_lowered` just to get the name of the â€śbody functionâ€ť `var"#f_kwarg#3"` as it looks like `(@which f_kwarg(a=1, b=2.0, c=:three)).specializations` is showing the specializations of the â€śkwsorterâ€ť function.

In that REPL session, AFAICT both keyword sorter and the body function are specialized (as I see `Tuple{Int64,Float64,Symbol}`). But I donâ€™t know if this is the right way to do it.

Yeah, it certainly does seem as if specialization is happening.

I am skeptical though. If specialization does not happen by default on varargs (thus requiring us to add the `Vararg` type parameter), why would it happen by default on keyword arguments?

I think the more likely situation is that we are seeing misleading output, and specialization is not actually happening here.

Yeah, I agree itâ€™s strange and thatâ€™s why I feel like Iâ€™m missing something.

But function call with keyword arguments is directly lowered to construction of a named tuple:

``````julia> Meta.@lower f_kwarg(a=1, b=2.0, c=:three)
:(\$(Expr(:thunk, CodeInfo(
@ none within `top-level scope'
1 â”€ %1 = Core.tuple(:a, :b, :c)
â”‚   %2 = Core.apply_type(Core.NamedTuple, %1)
â”‚   %3 = Core.tuple(1, 2.0, :three)
â”‚   %4 = (%2)(%3)
â”‚   %5 = Core.kwfunc(f_kwarg)
â”‚   %6 = (%5)(%4, f_kwarg)
â””â”€â”€      return %6
))))
``````

So maybe thatâ€™s why itâ€™s easier for the compiler to handle this?

Could you elaborate this? If you have multiple specializations of a method for different combinations of kwarg types how would you utilize, i.e. call, these different specializations if not through dispatch?

I recall reading a post by Jeff, Stefan, or some other core dev saying that code is specialized on kwarg types, just not dispatched on them. I cannot remember where it was, sorry.

Example:

``````julia> f(;kw) = values(kw)
f (generic function with 1 method)

julia> @code_llvm f(;kw = 1.0)

;  @ REPL[1]:1 within `f##kw'
top:
; â”Ś @ namedtuple.jl:95 within `getindex'
%1 = getelementptr inbounds [1 x double], [1 x double] addrspace(11)* %0, i64 0, i64 0
; â””
ret double %2
}

julia> @code_llvm f(;kw = 1)

;  @ REPL[1]:1 within `f##kw'
top:
; â”Ś @ namedtuple.jl:95 within `getindex'
%1 = getelementptr inbounds [1 x i64], [1 x i64] addrspace(11)* %0, i64 0, i64 0
; â””
ret i64 %2
}
``````

This is my recollection also.
kwargs are specialized on.
I believe it was a 1.0 change.
But I canâ€™t find it in the release notes.

1 Like

Thanks for the example. I understand that kwargs are specialized on. But how is this example different from actually having two methods and dispatching to either one based on the kwargs types?

Otherwise put, what is the difference between methods (in the dispatch sense), which we have one of in your example, and specializations, that is different natively compiled codes for a function (at least two in your example).

Isnâ€™t the fact that different specializations get called depending on the type of your kwargs just a form of dispatch?

I couldnâ€™t really say, but I suspect dispatch is much more complex than specialization. You can constrain the signature of a function as a whole in many ways that are not just the product of the types of each argument. For example `f(x::T, y::T) where {T}` establishes a correlation of argument types. I suspect the only reason for not allowing multiple dispatch on kwargs is due to this added complexity. If we just had simple dispatch like `f(x::Int, y::Float64)` it would indeed be quite the same as specialization, as far as I understand.
From reading this thread, it seems like specialization does occur on keyword arguments but no dynamic dispatch (which I donâ€™t totally understand because in my mental model that makes the specialization seem pointless). Is there a way to use `@nospecialize` on keyword arguments?