在 Julia 中使用变量后 for 循环明显变慢

vvo*_*dcx 3 julia

我对以下代码的性能感到非常困惑,在将“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)

关于原因以及如何防止这种延迟的任何想法?谢谢!

ffe*_*tte 6

这是因为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)