Почему перемещение цикла foor для понимания списка делает функцию нестабильной? - PullRequest
0 голосов
/ 14 июня 2019

Посмотрите на эти функции:

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 в качестве списка, оба параметра fB1 и fB2 нестабильны по типу и работают медленно, в то время как числовой результат остается (очевидно) тем же.

Почему это?

1 Ответ

1 голос
/ 14 июня 2019

Причина объяснена здесь в руководстве Юлии.

Важно знать, что понимание создает новую локальную область, как объяснено здесь в руководстве Юлии.

В этом случае, если вы продолжаете обновлять переменную из внешней области видимости внутри понимания (что, как правило, не рекомендуется, поскольку это обычно сбивает с толку людей, читающих такой код), лучшее, что вы можете сделать, насколько я знаю (может быть, кто-то может придумать лучшее решение, но я думаю, что это вряд ли, учитывая текущее состояние компилятора 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
...