Использование глобальных переменных в Джулии в целом медленное и должно дать вам скорость, сравнимую с R. Вы должны обернуть свой код в функцию, чтобы сделать его быстрым.
Вот время от моего ноутбука (я вырезал только соответствующую часть):
julia> function test()
trials = 100000
sample_size = 10;
sd = Array{Float64}(trials,sample_size-1)
tic()
for i = 2:sample_size
for j = 1:trials
res = randn(i)
sd[j,i-1] = (1/(i))*(sum(res.^2))-(1/((i)*i))*(sum(res)*sum(res))
end
end
toc()
end
test (generic function with 1 method)
julia> test()
elapsed time: 0.243233887 seconds
0.243233887
Кроме того, в Julia, если вы используете randn!
вместо randn
, вы можете ускорить его еще больше, поскольку вы избегаете перераспределения вектора res
(я не делаю другие оптимизации для кода, так как эта оптимизация отличается от Julia по сравнению с R все другие возможные ускорения в этом коде помогли бы Юлии и R аналогичным образом):
julia> function test2()
trials = 100000
sample_size = 10;
sd = Array{Float64}(trials,sample_size-1)
tic()
for i = 2:sample_size
res = zeros(i)
for j = 1:trials
randn!(res)
sd[j,i-1] = (1/(i))*(sum(res.^2))-(1/((i)*i))*(sum(res)*sum(res))
end
end
toc()
end
test2 (generic function with 1 method)
julia> test2()
elapsed time: 0.154881137 seconds
0.154881137
Наконец, лучше использовать пакет BenchmarkTools
для измерения времени выполнения в Юлии. Первые функции tic
и toc
будут удалены из Julia 0.7. Второе - вы смешиваете время компиляции и выполнения, если используете их (при двойном запуске функции test
вы увидите, что при втором запуске время уменьшается, поскольку Джулия не тратит время на компиляцию функций).
EDIT:
Вы можете сохранить trials
, sample_size
и sd
как глобальные переменные, но тогда вам следует добавить к ним префикс const
. Тогда достаточно обернуть цикл в функцию, подобную этой:
const trials = 100000;
const sample_size = 10;
const sd = Array{Float64}(trials,sample_size-1);
function f()
for i = 2:sample_size
for j = 1:trials
res = randn(i)
sd[j,i-1] = (1/(i))*(sum(res.^2))-(1/((i)*i))*(sum(res)*sum(res))
end
end
end
tic()
f()
toc()
Теперь для @parallel
:
Во-первых, вы должны использовать @sync
перед @parallel
, чтобы убедиться, что все работает правильно (т. Е. Что все рабочие закончили, прежде чем перейти к следующей инструкции). Чтобы понять, почему это необходимо, запустите следующий код в системе с несколькими работниками:
sd = SharedArray{Float64}(10^6);
@parallel for i = 1:2
if i < 2
sd[i] = 1
else
for j in 2:10^6
sd[j] = 1
end
end
end
minimum(sd) # most probably prints 0.0
sleep(1)
minimum(sd) # most probably prints 1.0
пока это
sd = SharedArray{Float64}(10^6);
@sync @parallel for i = 1:2
if i < 2
sd[i] = 1
else
for j in 2:10^6
sd[j] = 1
end
end
end
minimum(sd) # always prints 1.0
Во-вторых, улучшение скорости связано с @parallel
макросом, а не SharedArray
. Если вы попробуете свой код на Юлии с одним работником, это также будет быстрее. Короче говоря, причина в том, что @parallel внутренне оборачивает ваш код внутри функции. Вы можете проверить это, используя @macroexpand
:
julia> @macroexpand @sync @parallel for i = 2:sample_size
for j = 1:trials
res = randn(i)
sd[j,i-1] = (1/(i))*(sum(res.^2))-(1/((i)*i))*(sum(res)*sum(res))
end
end
quote # task.jl, line 301:
(Base.sync_begin)() # task.jl, line 302:
#19#v = (Base.Distributed.pfor)(begin # distributed\macros.jl, line 172:
function (#20#R, #21#lo::Base.Distributed.Int, #22#hi::Base.Distributed.Int) # distributed\macros.jl, line 173:
for i = #20#R[#21#lo:#22#hi] # distributed\macros.jl, line 174:
begin # REPL[22], line 2:
for j = 1:trials # REPL[22], line 3:
res = randn(i) # REPL[22], line 4:
sd[j, i - 1] = (1 / i) * sum(res .^ 2) - (1 / (i * i)) * (sum(res) * sum(res))
end
end
end
end
end, 2:sample_size) # task.jl, line 303:
(Base.sync_end)() # task.jl, line 304:
#19#v
end