Причина объяснена здесь в руководстве Юлии.
Важно знать, что понимание создает новую локальную область, как объяснено здесь в руководстве Юлии.
В этом случае, если вы продолжаете обновлять переменную из внешней области видимости внутри понимания (что, как правило, не рекомендуется, поскольку это обычно сбивает с толку людей, читающих такой код), лучшее, что вы можете сделать, насколько я знаю (может быть, кто-то может придумать лучшее решение, но я думаю, что это вряд ли, учитывая текущее состояние компилятора Julia) - использовать аннотацию типа:
function fB2(n)
x::Float64 = one(Float64)
[x = x+x for i in 1:n]
return x
end
Это не позволит избежать бокса, но должно сделать возвращаемый тип выводимым, а производительность должна значительно улучшиться.
В будущем весьма вероятно, что компилятор 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