Performance of generator expressions and higher order functions

I don’t get it. I used to believe that the following two versions are lowered identically (I just made all scopes and loops explicit):

julia> function v5a(n :: Int)
           prime = Array{Bool}(undef, n)
           fill!(prime, true)
           rr=2:n
           it = iterate(rr)
           @label loopstart
           (it === nothing) && @goto loopend
           let (i,s) = it
           if any(i%j==0 for j in 2:i-1)
               prime[i]=false
           end
           it = iterate(rr,s)
           @goto loopstart
           end
           @label loopend
           return prime
       end

julia> function v5b(n :: Int)
           prime = Array{Bool}(undef, n)
           fill!(prime, true)
           rr=2:n
           it = iterate(rr)
           @label loopstart
           (it === nothing) && @goto loopend
           let 
           (i,s) = it
           if any(i%j==0 for j in 2:i-1)
               prime[i]=false
           end
           it = iterate(rr,s)
           @goto loopstart
           end
           @label loopend
           return prime
       end

However,

julia> N=1000; v5a(N);v5b(N);@time v5a(N);@time v5b(N);
  0.000459 seconds (5 allocations: 1.219 KiB)
  0.007087 seconds (22.20 k allocations: 379.313 KiB)

Imo both variants should be identical after lowering (before inference)?

Or is there any difference in semantics between let a = b \n body end and let \n a = b \n body end, under the assumption that the identifier a is not present in the outer function scope (which is know during lowering!)?

If there is no semantic difference, then there is quite a bit of pre-inference optimization potential.

1 Like