Est*_*ban 5 arrays performance sum julia
好的,我最近正在进行一系列测试.我有一个MC模拟,我有几个变量(20),将它们全部放在一维数组中是有意义的,因为它使几个更容易阅读.
但是我有一个问题,我需要在每次迭代中对变量求和,并且模拟需要大量的迭代,所以我遇到了这个问题(减少到7个变量):
function sumtest1(N)
s=0.0
a=1.0
b=2.0
c=3.0
d=4.0
e=5.0
f=6.0
g=7.0
for i = 1:N
s += (a+b+c+d+e+f+g)
end
return s
end
function sumtest2(N)
s=0.0
A=[1.0,2.0,3.0,4.0,5.0,6.0,7.0]
for i = 1:N
s += sum(A)
end
return s
end
@time sumtest1(1_000_000_000)
elapsed time: 0.998272756 seconds (96 bytes allocated)
@time sumtest1(1_000_000_000)
elapsed time: 7.522198967 seconds (208 bytes allocated)
Run Code Online (Sandbox Code Playgroud)
这是预期的吗?或者我做错了什么?我真的希望将我的变量编入索引,因为其他原因现在解释的时间太长了,但这种性能损失是我无法忍受的.
Ste*_*ski 11
让我们看一下正在执行的LLVM代码sumtest1:
julia> @code_llvm sumtest1(10^9)
define double @julia_sumtest1_21391(i64) {
top:
%1 = icmp sgt i64 %0, 0
%2 = select i1 %1, i64 %0, i64 0
%3 = icmp eq i64 %2, 0
br i1 %3, label %L3, label %L.preheader
L.preheader: ; preds = %top
%4 = icmp sgt i64 %0, 0
%smax = select i1 %4, i64 %0, i64 0
br label %L
L: ; preds = %L, %L.preheader
%lsr.iv = phi i64 [ %smax, %L.preheader ], [ %lsr.iv.next, %L ]
%s.0 = phi double [ %5, %L ], [ 0.000000e+00, %L.preheader ]
%5 = fadd double %s.0, 2.800000e+01
%lsr.iv.next = add i64 %lsr.iv, -1
%6 = icmp eq i64 %lsr.iv.next, 0
br i1 %6, label %L3, label %L
L3: ; preds = %L, %top
%s.1 = phi double [ 0.000000e+00, %top ], [ %5, %L ]
ret double %s.1
}
Run Code Online (Sandbox Code Playgroud)
这很难理解,但有一点在循环体中突出L:
%5 = fadd double %s.0, 2.800000e+01
Run Code Online (Sandbox Code Playgroud)
对于每次迭代,预先计算的常量28.0被添加到累加器中s.编译器可以告诉您永远不会更改任何局部变量,因此它知道每次都添加相同的总和.循环必须执行的唯一原因是重复的浮点加法并不完全等同于乘法.如果所有局部变量都改为整数,重复加法完全等同于乘法,则完全消除循环:
julia> @time sumtest1_int(10^9)
0.000005 seconds (6 allocations: 192 bytes)
28000000000
julia> @code_llvm sumtest1_int(10^9)
define i64 @julia_sumtest1_int_21472(i64) {
top:
%1 = icmp slt i64 %0, 1
br i1 %1, label %L3, label %L.preheader
L.preheader: ; preds = %top
%2 = icmp sgt i64 %0, 0
%.op = mul i64 %0, 28
%3 = select i1 %2, i64 %.op, i64 0
br label %L3
L3: ; preds = %L.preheader, %top
%s.1 = phi i64 [ 0, %top ], [ %3, %L.preheader ]
ret i64 %s.1
}
Run Code Online (Sandbox Code Playgroud)
这大致翻译成朱莉娅:
sumtest1_int(N) = N < 1 ? 0 : ifelse(N > 0, N*28, 0)
Run Code Online (Sandbox Code Playgroud)
这有点多余,因为身体可以简化为ifelse(N > 1, N*28, 0)(28N因为我们不关心负值N,因此可以改为),但它仍然比执行循环快得多.
sumtest2几乎不容易分析或优化该功能.这需要证明阵列A永远不会被改变,这是非常困难的.因此,编译器别无选择,只能完成所有工作,当然,这比不执行任务要慢得多.在模拟中,使用局部变量可能比在数组中存储值更快,但可能没有.您将不得不测量代码,这些代码可以更难以完全优化,以确保完全无法优化.