当前位置:K88软件开发文章中心编程语言非主流编程语言Julia → 文章内容

Julia 代码性能优化

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 16:28:31

+= ret[2] end yend```和```function xinc!{T}(ret::AbstractVector{T}, x::T) ret[1] = x ret[2] = x+1 ret[3] = x+2 nothingendfunction loopinc_prealloc() ret = Array(Int, 3) y = 0 for i = 1:10^7 xinc!(ret, i) y += ret[2] end yend```计时结果:``` julia> @time loopinc() elapsed time: 1.955026528 seconds (1279975584 bytes allocated) 50000015000000 julia> @time loopinc_prealloc() elapsed time: 0.078639163 seconds (144 bytes allocated) 50000015000000```预先分配有其他好处,比如,允许访问者通过算法控制“输出”类型。在上面的例子中,我们可以按照自己希望的,通过一个 ``SubArray`` 而不是 ``Array``。按着最极端的来想,预先分配可以让你的代码看起来丑点,所以需要一些表达方式和判断。 ## 避免输入/输出时的串插入 把数据写入文件(或者其他输入/输出设备)时,中间字符串的形成是额外的开销。而不是: ``` println(file, "$a $b")```使用:``` println(file, a, " ", b)```第一种代码形成了一个字符串,然后把它写入了文件,而第二种代码直接把值写入了文件。同样也注意到在某些情况下,字符串的插入很难读出来。考虑一下: ``` println(file, "$(f(a))$(f(b))")```对比:``` println(file, f(a), f(b))```## 处理有关舍弃的警告被舍弃的函数,会查表并显示一次警告,而这会影响性能。建议按照警告的提示进行对应的修改。## 小技巧注意些有些小事项,能使内部循环更紧致。- 避免不必要的数组。例如,不要使用 ``sum([x,y,z])`` ,而应使用 ``x+y+z``- 对于较小的整数幂,使用 ``*`` 更好。如 ``x*x*x`` 比 ``x^3`` 好- 针对复数 ``z`` ,使用 ``abs2(z)`` 代替 ``abs(z)^2`` 。一般情况下,对于复数参数,尽量用 ``abs2`` 代替 ``abs``- 对于整数除法,使用 ``div(x,y)`` 而不是 ``trunc(x/y)``, 使用 ``fld(x,y)`` 而不是 ``floor(x/y)``, 使用 ``cld(x,y)`` 而不是 ``ceil(x/y)``.## 性能注释有时你可以设定某些项目属性来获得更好的优化。 - 在检查公式时,使用 ``@inbounds`` 来消除数组界限。一定要在这之前完成。如果下标越界了,你可能会遇到崩溃或不执行的问题。- 在 ``for`` 循环之前写上 ``@simd``,这个可以帮你检验。**这个特征是试验性的**而且在之后的 Julia 版本中可能会改变会消失。这里有一个包含两种形式审定的例子: ``` function inner( x, y ) s = zero(eltype(x)) for i=1:length(x) @inbounds s += x[i]*y[i] end s end function innersimd( x, y ) s = zero(eltype(x)) @simd for i=1:length(x) @inbounds s += x[i]*y[i] end s end function timeit( n, reps ) x = rand(Float32,n) y = rand(Float32,n) s = zero(Float64) time = @elapsed for j in 1:reps s+=inner(x,y) end println("GFlop = ",2.0*n*reps/time*1E-9) time = @elapsed for j in 1:reps s+=innersimd(x,y) end println("GFlop (SIMD) = ",2.0*n*reps/time*1E-9) end timeit(1000,1000)```在配有 2.4GHz 的 Intel Core i5 处理器的电脑上,产生如下结果: ``` GFlop = 1.9467069505224963 GFlop (SIMD) = 17.578554163920018`````@simd for`` 循环应该是一维范围的。*缩减变数* 是用于累积变量的,比如例子中的 ``s``。通过使用 ``@simd``,你可以维护循环的几种性能: - 有缩减变数的特殊考虑后,在任意的或重叠的顺序中执行迭代都是安全的。- 减少变量的浮点操作可以被重复执行,但是可能会比没有 ``@simd`` 产生不同的结果。 - 不会有一个迭代在等待另一个迭代,以实现前进。 使用 ``@simd`` 仅仅是给了编译器矢量化的通行证。它是不是真的会这样做还取决于编译器。要真正从当前的实现中获益,你的循环应该有如下额外的性能: - 循环必须是内部循环。- 循环主题必须是无循环程序。这就是为什么当前所有的数组访问都需要 ``@inbounds`` 的原因了。- 访问必须有一个跨越模式,而且不能“聚集”(随机指针读取)或者“分散”(随机指针写入)。- 跨越应该是单元跨越。- 在一些简单的例子中,例如一个 2-3 数组访问的循环中,LLVM 自动矢量化可能会自动生效,导致无需 ``@simd`` 的进一步加速。

上一页  [1] [2] 


Julia 代码性能优化