From what I see, both methods also seem to produce the same LLVM code.
@code_llvm
lies about specialization:
Note that @code_typed and friends will always show you specialized code, even if Julia would not normally specialize that method call. You need to check the method internals if you want to see whether specializations are generated when argument types are changed, i.e., if Base.specializations(@which f(…)) contains specializations for the argument in question.
(source)
However, that’s not what’s causing the issue.
Chairmarks tells the compiler the types (and only the types) of pipeline arguments and interpolated values. For example, if you are benchmarking @b 7 cbrt
, it will tell the compiler that you want to cube root an integer. @b cbrt($7)
is the same. If you literally interpolate the 7
(e.g. @b cbrt(7)
) then the compiler will know the type and value of that argument, in which case it can constant propagate and effectively compute @b _ -> 1.9129311827723892
instead. If you want true interpolation without writing it out yourself, you can use @eval
. For example x = 7; @eval @b cbrt($x)
is the same as @b cbrt(7)
.
In the case of @b foo($T)
, the type of T
is DataType
so the compiler does not know whether foo
will dispatch to ConcreteTrait()
or throw a method error. foo2
, on the other hand, always returns ConcreteTrait()
when passed a DataType
so it is fast even though all the compiler knows about T
is that it’s a DataType
.
Perhaps Chairmarks should tell the compiler the exact value of interpolated values and pipeline arguments when they are types? But then I don’t know how one would opt out of that behavior while the current behavior can be avoided with @eval
interpolation. I’m open to feedback and/or changing the behavior on this edge case if folks have ideas.
Aside @b foo(T)
reports a fast runtime as well, which means chiarmarks is claiming that a function defined at global scope which calls foo(T)
will run quickly, even though T
is a non-constant global. This seems implausible to me but…
julia> g() = foo(T)
g (generic function with 1 method)
julia> @b g
1.134 ns
julia> @btime g()
1.083 ns (0 allocations: 0 bytes)
ConcreteTrait()
…I guess there’s a back-edge or something?