Skip to content

Add an option to ensure that @nospecialize really does avoid specialization #40312

Closed

Description

In JuliaData/DataFrames.jl#2691 I needed to use Ref{Any} trick to avoid excessive specialization. Unfortunately I was not able to achieve the required level of despecialization using @nospecialize. The issue is quite complex and we discussed with @nalimilan a lot what to do with it. Since we really do not understand in full how to avoid specialization I ask the question here (as on Slack it probably will get quickly lost).

The problem is that @nospecialize does not guarantee that the argument is not specialized. There are cases when not having this is prohibitive (as each additional compilation of method instance is costly in itself). Here is an MWE using MethodAnalysis.jl:

julia> using MethodAnalysis
julia> @noinline f(@nospecialize(x)) = map(x, [1,2,3])
f (generic function with 1 method)
julia> g(x) = f(x)
g (generic function with 1 method)
julia> g(sin);
julia> methodinstances(f)
2-element Vector{Core.MethodInstance}:
 MethodInstance for f(::Function)
 MethodInstance for f(::Any)
julia> g(Int);
julia> methodinstances(f)
3-element Vector{Core.MethodInstance}:
 MethodInstance for f(::Function)
 MethodInstance for f(::Any)
 MethodInstance for f(::Type)
julia> g(Float64);
julia> methodinstances(f)
3-element Vector{Core.MethodInstance}:
 MethodInstance for f(::Function)
 MethodInstance for f(::Any)
 MethodInstance for f(::Type)

and I get 3 method instances of f instead of 1 as I wanted.

Now in a fresh session additionally this happens:

julia> using MethodAnalysis
julia> @noinline f(@nospecialize(x)) = map(x, [1,2,3])
f (generic function with 1 method)
julia> g(x::Base.Callable) = f(x)
g (generic function with 1 method)
julia> g(sin);
julia> methodinstances(f)
2-element Vector{Core.MethodInstance}:
 MethodInstance for f(::Function)
 MethodInstance for f(::Any)
julia> g(Int);
julia> methodinstances(f)
3-element Vector{Core.MethodInstance}:
 MethodInstance for f(::Function)
 MethodInstance for f(::Any)
 MethodInstance for f(::Type{Int64})
julia> g(Float64);
julia> methodinstances(f)
4-element Vector{Core.MethodInstance}:
 MethodInstance for f(::Function)
 MethodInstance for f(::Any)
 MethodInstance for f(::Type{Int64})
 MethodInstance for f(::Type{Float64})

and this time it is even worse - for each type passed a new method instance is generated.

I have more examples if needed, but they all boil down to one issue: how to tell the compiler that unconditionally only one method instance should be generated for a given argument (as commented above in JuliaData/DataFrames.jl#2691 we ended up using Ref{Any} to guarantee this, but this is kind of ugly and maybe there is a better way to do it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions