为什么这个简单的索引变量在 Julia 中被 gettng Box-ed?

Why is this simple indexing variable gettng Box-ed in Julia?

在从 Project Euler 手动求解 Problem 10 时(而不是简单地 using Primes),我使用 BitVector 在 Julia 中实现了一个朴素的埃拉托色尼筛法。它产生了正确的输出,但是当我使用 @code_warntype 检查其类型稳定性时,我发现 i 被分配为 Core.Box,这会影响 psum 的类型,并且这反过来又会破坏整个函数的类型稳定性。这是使用的代码:

function primesum(limit::T = 2_000_000) where T<:Integer
    #easy way: using Primes; sum(primes(limit)) 

    #hard way: 
    is_prime_num = BitVector(limit)
    is_prime_num .= true

    psum = zero(limit)
    for i in 2:limit
        if is_prime_num[i]
            psum += i
            multiples_of_i = [k*i for k in 2:(limit÷i)]
            is_prime_num[multiples_of_i] .= false
        end
    end
    psum
end

这里是 code_warntype 输出的变量部分(完整输出 here):

Variables:
  #self#::#primesum
  limit::Int64
  #46::##46#47
  i::Core.Box
  multiples_of_i::Any
  #temp#@_6::Int64
  is_prime_num::BitArray{1}
  psum::Any
  J::Any
  #temp#@_10::Any

总的来说,代码中的很多类型(包括函数的 return 类型)都保留为 Anywhere _ 类型。

我能够通过将 for 循环行更改为 for i::T in 2:limit 来提高速度(几乎 10 倍)和内存(~3 倍)- 然后,i 仍然被设置为Core.Box,但这得到 typeasserted 到 Int64 并且不会将不稳定性传播到 psum 和其他人。但是 我更感兴趣的是为什么 Julia 不能推断 i 的类型而不是加速这个特定的代码。 我习惯于输入不稳定性至少是有意义的回想起来,但这一个似乎非常清晰且易于推断,所以我想知道这里哪里存在类型歧义。

一个临时解决方案是将 i 包装在 let 块中围绕理解(我还建议对您的代码进行一些小的调整,这些调整是较小的清理 - 我已经创建了 multiples_of_i 因为这是你问题的核心但实际上使用这个变量也是低效的 - 最好使用循环在适当的地方将 is_prime_num 向量设置为 false):

function primesum(limit::Integer = 2_000_000)
    is_prime_num = trues(limit)
    psum = zero(limit)
    for i in 2:limit
        if is_prime_num[i]
            psum += i
            let i=i
                multiples_of_i = [k*i for k in 2:(limit÷i)]
                is_prime_num[multiples_of_i] .= false
            end
        end
    end
    psum
end

希望在长期 运行 中,问题 https://github.com/JuliaLang/julia/issues/15276 将得到解决,这将不再需要。