为什么将 for 循环移动到列表理解会使函数类型不稳定?

Why moving a foor loop to list comprehension makes a function type-unstable?

看看这些功能:

function fA1(s::AbstractString, n)
    T = getfield(Base, Symbol(s)) 
    x = one(T)
    for i in 1:n
        x = x+x
    end
    return x
end
function fA2(s::AbstractString, n)
    T = getfield(Base, Symbol(s)) 
    x = one(Float64)
    for i in 1:n
        x = x+x
    end
    return x
end
function fB1(s::AbstractString, n)
    T = getfield(Base, Symbol(s)) 
    x = one(T)
    [x = x+x for i in 1:n]
    return x
end
function fB2(s::AbstractString, n)
    T = getfield(Base, Symbol(s)) 
    x = one(Float64)
    [x = x+x for i in 1:n]
    return x
end

fA1 类型不稳定且速度慢,fA2 类型稳定且速度快。 但是,当我将 for 循环作为列表理解移动时,fB1fB2 都是类型不稳定且缓慢的,而数值结果(显然)保持不变。

这是为什么?

Julia 手册中解释了原因 here

重要的是要知道理解会创建一个新的本地范围,如 Julia 手册中所述here

在这种情况下,如果你坚持从外部作用域更新变量(通常不推荐这样做,因为阅读此类代码的人通常会感到困惑),你能做的最好的就是我知道(也许有人可以想出更好的解决方案,但考虑到 Julia 编译器的当前状态,我认为这不太可能)是使用类型注释:

function fB2(n)
    x::Float64 = one(Float64)
    [x = x+x for i in 1:n]
    return x
end

这不会避免装箱,但应该使 return 类型可推断并且性能应该显着提高。

将来 Julia 编译器很可能会足够智能,无需类型注释即可处理此类代码。

这是性能比较:

julia> using BenchmarkTools

julia> function f_fast(n)
           x::Float64 = one(Float64)
           [x = x+x for i in 1:n]
           return x
       end
f_fast (generic function with 1 method)

julia> function f_slow(n)
           x = one(Float64)
           [x = x+x for i in 1:n]
           return x
       end
f_slow (generic function with 1 method)

julia> @benchmark f_fast(1000)
BenchmarkTools.Trial:
  memory estimate:  23.63 KiB
  allocs estimate:  1004
  --------------
  minimum time:     4.357 μs (0.00% GC)
  median time:      7.257 μs (0.00% GC)
  mean time:        10.314 μs (16.54% GC)
  maximum time:     5.256 ms (99.86% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark f_slow(1000)
BenchmarkTools.Trial:
  memory estimate:  23.66 KiB
  allocs estimate:  1005
  --------------
  minimum time:     17.899 μs (0.00% GC)
  median time:      26.300 μs (0.00% GC)
  mean time:        34.916 μs (15.56% GC)
  maximum time:     36.220 ms (99.91% GC)
  --------------
  samples:          10000
  evals/sample:     1