我对以下代码的性能感到非常困惑,在将“2”替换为变量“z=2”后,for 循环的运行速度要慢得多:
julia> @elapsed for a=1:2:24996
for b=1:2:24996
end
end
2.0e-7
julia> z=2
2
julia> @elapsed for a=1:z:24996
for b=1:z:24996
end
end
14.455516599
Run Code Online (Sandbox Code Playgroud)
关于原因以及如何防止这种延迟的任何想法?谢谢!
这是因为z在全局范围内定义并且不是常量,这意味着它可以随时更改值。更重要的是,它也可以改变类型。这可以防止 Julia 编译器进行大量优化。
TLDR:始终尝试避免在全局范围内使用非常量变量进行性能关键操作!
但是,如果您将值定义为常量(w下面的示例)或您的变量定义在局部范围内(y下面的示例),那么循环将被编译为代码,就像使用文字一样高效:
julia> @elapsed for a=1:2:24996
for b=1:2:24996
end
end
1.16e-7
Run Code Online (Sandbox Code Playgroud)
julia> z=2
2
julia> @elapsed for a=1:z:24996
for b=1:z:24996
end
end
10.726003104
Run Code Online (Sandbox Code Playgroud)
julia> const w = 2
2
julia> @elapsed for a=1:w:24996
for b=1:w:24996
end
end
1.58e-7
Run Code Online (Sandbox Code Playgroud)
julia> @elapsed let y=2
for a=1:y:24996
for b=1:y:24996
end
end
end
1.05e-7
Run Code Online (Sandbox Code Playgroud)
另请注意,您的基准测试存在一些问题。
首先,您的基准测试不计算任何内容。这是一个问题,因为一旦编译器可以自由进行任何优化,它就会优化所有内容。
您在此处测量的运行时间与编译的关系比与问题的大小有关。例如,当问题规模增加时,下面会发生什么:
julia> @elapsed for a=1:2:2*24996
for b=1:2:2*24996
end
end
1.21e-7
julia> @elapsed for a=1:2:4*24996
for b=1:2:4*24996
end
end
1.31e-7
Run Code Online (Sandbox Code Playgroud)
其次,建议对封装在函数中的代码执行此类微基准测试,并使用由BenchmarkTools(例如BenchmarkTools.@belapsed应该首选@elapsed)提供的宏,以便可以执行更准确的测量,并省略编译时间。
解决所有这些问题,我们以更令人信服的方式看到,在函数内部具有局部变量的代码与具有常量文字的代码一样快:
julia> function foo(n)
s = 0.
for a=1:2:n
for b=1:2:n
s += a+b
end
end
s
end
foo (generic function with 1 method)
julia> @btime foo(24996)
224.787 ms (0 allocations: 0 bytes)
3.904375299984e12
julia> function foo(n, z)
s = 0.
for a=1:z:n
for b=1:z:n
s += a+b
end
end
s
end
foo (generic function with 2 methods)
julia> @btime foo(24996, 2)
224.762 ms (0 allocations: 0 bytes)
3.904375299984e12
Run Code Online (Sandbox Code Playgroud)