通过示例优化 Julia 代码
Optimize Julia Code by Example
我目前正在用 Julia 编写数值求解器。我认为它背后的数学并不重要。这一切都归结为一个事实,即一个特定的操作被执行了几次并且使用了很大一部分(~80%)的 运行 时间。
我尽量减少它并向您展示这段代码,它可以保存为 dummy.jl
然后通过 include("dummy.jl")
执行 dummy(10)
(对于编译)然后 dummy(1000)
.
function dummy(N::Int64)
A = rand(N,N)
@time timethis(A)
end
function timethis(A::Array{Float64,2})
dummyvariable = 0.0
for k=1:100 # just repeat a few times
for i=2:size(A)[1]-1
for j=2:size(A)[2]-1
dummyvariable += slopefit(A[i-1,j],A[i,j],A[i+1,j],2.0)
dummyvariable += slopefit(A[i,j-1],A[i,j],A[i,j+1],2.0)
end
end
end
println(dummyvariable)
end
@inline function minmod(x::Float64, y::Float64)
return sign(x) * max(0.0, min(abs(x),y*sign(x) ) );
end
@inline function slopefit(left::Float64,center::Float64,right::Float64,theta::Float64)
# arg=ccall((:minmod,"libminmod"),Float64,(Float64,Float64),0.5*(right-left),theta*(center-left));
# result=ccall((:minmod,"libminmod"),Float64,(Float64,Float64),theta*(right-center),arg);
# return result
tmp = minmod(0.5*(right-left),theta*(center-left));
return minmod(theta*(right-center),tmp);
#return 1.0
end
这里,timethis
模仿一下我花了不少时间的代码部分。我注意到,slopefit
执行起来非常昂贵。
例如,dummy(1000)
在我的机器上大约需要 4 秒。相反,如果 slopefit
总是 return 1
并且不计算任何东西,则时间会减少到总时间的十分之一。
现在,显然天下没有免费的午餐。
我知道,这只是一项代价高昂的操作。但我仍然会尽可能地优化它,因为很多时间都花在了一些看起来很容易优化的事情上,因为它只是几行代码。
到目前为止,我尝试将 minmod
和 slopefit
实现为 C 函数并调用它们,但是这只会增加计算时间(也许我做错了)。
所以我的问题是,我有什么可能性来优化slopefit
的调用?
请注意,在实际代码中,slopefit
的参数不是此处提到的参数,而是取决于条件语句,这使得一切都难以矢量化(如果这会带来任何性能提升我不是当然)。
我能想到两个级别的优化。
首先:minmod
的以下实现会更快,因为它避免了分支(我知道这是您想要的功能):
@inline minmod(x::Float64, y::Float64) = ifelse(x<0, clamp(y, x, 0.0), clamp(y, 0.0, x))
其次:你可以使用@inbounds
来加快循环速度:
@inbounds for i=2:size(A)[1]-1
我目前正在用 Julia 编写数值求解器。我认为它背后的数学并不重要。这一切都归结为一个事实,即一个特定的操作被执行了几次并且使用了很大一部分(~80%)的 运行 时间。
我尽量减少它并向您展示这段代码,它可以保存为 dummy.jl
然后通过 include("dummy.jl")
执行 dummy(10)
(对于编译)然后 dummy(1000)
.
function dummy(N::Int64)
A = rand(N,N)
@time timethis(A)
end
function timethis(A::Array{Float64,2})
dummyvariable = 0.0
for k=1:100 # just repeat a few times
for i=2:size(A)[1]-1
for j=2:size(A)[2]-1
dummyvariable += slopefit(A[i-1,j],A[i,j],A[i+1,j],2.0)
dummyvariable += slopefit(A[i,j-1],A[i,j],A[i,j+1],2.0)
end
end
end
println(dummyvariable)
end
@inline function minmod(x::Float64, y::Float64)
return sign(x) * max(0.0, min(abs(x),y*sign(x) ) );
end
@inline function slopefit(left::Float64,center::Float64,right::Float64,theta::Float64)
# arg=ccall((:minmod,"libminmod"),Float64,(Float64,Float64),0.5*(right-left),theta*(center-left));
# result=ccall((:minmod,"libminmod"),Float64,(Float64,Float64),theta*(right-center),arg);
# return result
tmp = minmod(0.5*(right-left),theta*(center-left));
return minmod(theta*(right-center),tmp);
#return 1.0
end
这里,timethis
模仿一下我花了不少时间的代码部分。我注意到,slopefit
执行起来非常昂贵。
例如,dummy(1000)
在我的机器上大约需要 4 秒。相反,如果 slopefit
总是 return 1
并且不计算任何东西,则时间会减少到总时间的十分之一。
现在,显然天下没有免费的午餐。
我知道,这只是一项代价高昂的操作。但我仍然会尽可能地优化它,因为很多时间都花在了一些看起来很容易优化的事情上,因为它只是几行代码。
到目前为止,我尝试将 minmod
和 slopefit
实现为 C 函数并调用它们,但是这只会增加计算时间(也许我做错了)。
所以我的问题是,我有什么可能性来优化slopefit
的调用?
请注意,在实际代码中,slopefit
的参数不是此处提到的参数,而是取决于条件语句,这使得一切都难以矢量化(如果这会带来任何性能提升我不是当然)。
我能想到两个级别的优化。
首先:minmod
的以下实现会更快,因为它避免了分支(我知道这是您想要的功能):
@inline minmod(x::Float64, y::Float64) = ifelse(x<0, clamp(y, x, 0.0), clamp(y, 0.0, x))
其次:你可以使用@inbounds
来加快循环速度:
@inbounds for i=2:size(A)[1]-1