Previously I asked about, and solved, a problem with compilation of a function inside generated code - the problem was that multiple instances of a function were getting compiled without being called. I would like to avoid this. The solution, which I now realize has a time penalty, was to use the @nospecialize
macro.
I now want to use a @generated
function, so that I can set the upper and lower bounds programmatically. If you run this MWE, you’ll see that 32 function instances are compiled, but only 1 is called:
function valuedispatch_expr(::Val{lower}, ::Val{upper}, val, fun) where {lower, upper}
if lower >= upper
return :( return $fun(Val($upper)) )
end
midpoint = lower + div(upper - lower, 2)
expr_a = valuedispatch_expr(Val(lower), Val(midpoint), val, fun)
expr_b = valuedispatch_expr(Val(midpoint+1), Val(upper), val, fun)
return quote
if $val <= $midpoint
$expr_a
else
$expr_b
end
end
end
macro valuedispatch_macro(lower::Int, upper::Int, val, fun)
return valuedispatch_expr(Val(lower), Val(upper), esc(val), esc(fun))
end
@generated function valuedispatch(::Val{lower}, ::Val{upper}, val, fun) where {lower, upper}
ex = :( @valuedispatch_macro($lower, $upper, val, fun) )
return quote
@nospecialize
$ex
end
end
@generated function myfun(::Val{v}) where v
println("Compiling ", v)
return :(v, println("Running ", $v))
end
println(valuedispatch(Val(1), Val(32), 3, myfun))
Does anyone have any ideas how I might fix this? How might I make sure that only the instance of myfun
that gets compiled is the one that is actually called?
On a side note, I think this valuedispatch
function will be really useful functionality in general, if I can iron out the issues. For example, in the following case it’s 33x faster than variable dispatch (on my machine):
function myfun2(::Val{v}) where v
return v*v-v
end
@btime valuedispatch(Val(1), Val(8), Int(ceil(rand() * 8.)), myfun2)
@btime myfun2(Val(Int(ceil(rand() * 8.))))