Julia v1.8 -> v1.9 makes this code 1000x slower

On upgrading to Julia from 1.8.5. to 1.9.0 I noticed code in one of my packages slow down significantly. It has taken me a long time to get down to the simplest code which replicates the problem, but I have it now. After upgrading, the following code has become type unstable, and 1000x slower as a result:

using StaticArrays, BenchmarkTools

@inline function countvars(bitflags, var)
    return bitflags >> 0

function myfunc(::Val{bitflags}, var) where bitflags
    N = countvars(bitflags, var)
    return zeros(SVector{N, Float64})

struct A
a = A(0.)

@btime myfunc(Val(1), $a);
@code_warntype myfunc(Val(1), a);

Moreover, this effect is very unstable - seemingly insignificant changes to the code result in the type stability disappearing. For example:

  • Removing data from struct A
  • Moving the code from countvars directly into myfunc
  • Removing the redundant var from the inputs to countvars
  • Changing the >> in line 4 to something like & or +

I have raised an issue about this in GitHub, but wanted to see if anyone else had seen anything similar. Is this expected, or unexpected?


I imagine this is something to do with constant propagation. To be explicit you could try:

@inline Base.@constprop :aggressive function countvars(bitflags, var)
    return bitflags >> 0

EDIT: Corrected the syntax thanks to @aviatesk, but still doesn’t seem to change anything.

Yeah, that’s really weird. I can confirm the regression exists on master as well. One minor simplification is to change myfunc to

function myfunc(::Val{bitflags}, var) where bitflags
    N = countvars(bitflags, var)
    return Array{Float64, N}

which removes the dependency on StaticArrays. The part of this that makes no sense to me is that the compiler knows that countvars is constant foldable, but for some reason isn’t constant folding it.

This is not a valid Base.@constprop annotation. Base.@constprop can only be applied on a method definition, not on callsites. And the @inline annotation is enough to encourage constant propagation.

Really? I think it only exists on 1.9 only. And it looks like something about semi-concrete abstract interpretation. Since we add many refinements on it after branching on 1.9, I guess the inference regression has been resolved on 1.10 but still there for 1.9.


This throws an error for me:

ERROR: LoadError: countvars(bitflags, var) is not a function expression

I see it on a 10 day old master.

1 Like

I have found another performance regression moving from Julia v1.8.5 to v1.9.0. Some code now allocates memory, when none was allocated using v1.8.5. This makes the code 5x slower. There doesn’t appear to be any type instability. I have created an issue here.

Is there any information on what has changed? I guess it some under-the-hood compiler inference algorithms.