我刚开始使用Julia并将我的MATLAB代码翻译成Julia(基本上是逐行) . 我注意到Julia代码要慢得多(比如50x) . 最初的问题是一个动态编程问题,其中我插入了值函数 - 插值是代码大部分时间都在使用的位置 . 所以我尝试制作一个显示性能差异的最小示例代码 . 需要注意的重要事项是,它是插值的样条近似,并且网格最好是不规则的,即不是等间距的 . MATLAB代码:
tic
spacing=1.5;
Nxx = 300;
Naa = 350;
Nalal = 200;
sigma = 10;
NoIter = 500;
xx=NaN(Nxx,1);
xmin = 0.01;
xmax = 400;
xx(1) = xmin;
for i=2:Nxx
xx(i) = xx(i-1) + (xmax-xx(i-1))/((Nxx-i+1)^spacing);
end
f_U = @(c) c.^(1-sigma)/(1-sigma);
W=NaN(Nxx,1);
W(:,1) = f_U(xx);
xprime = ones(Nalal,Naa);
for i=1:NoIter
W_temp = interp1(xx,W(:,1),xprime,'spline');
end
toc
Elapsed time is 0.242288 seconds.
朱莉娅代码:
using Dierckx
function performance()
const spacing=1.5
const Nxx = 300
const Naa = 350
const Nalal = 200
const sigma = 10
const NoIter = 500
xx=Array(Float64,Nxx)
xmin = 0.01
xmax = 400
xx[1] = xmin
for i=2:Nxx
xx[i] = xx[i-1] + (xmax-xx[i-1])/((Nxx-i+1)^spacing)
end
f_U(c) = c.^(1-sigma)/(1-sigma)
W=Array(Float64,Nxx,1)
W[:,1] = f_U(xx)
W_temp = Array(Float64,Nalal,Naa)
interp_spline = Spline1D(xx,W[:,1])
xprime = ones(Nalal,Naa)
for i=1:NoIter
for j=1:Naa
for k=1:Nalal
W_temp[k,j] = evaluate(interp_spline, xprime[k,j])
end
end
end
end
@time(performance())
4.200732 seconds (70.02 M allocations: 5.217 GB, 4.35% gc time)
我把W做成了一个二维数组,因为在最初的问题中它是一个矩阵 . 我对不同的插值包进行了一些研究,但对于不规则网格和样条曲线没有太多选择 . MATLAB的interp1显然不可用 .
我的问题是我在想如果我编写Julia代码并且它给出与MATLAB相同的结果,那么Julia应该更快 . 但显然事实并非如此,所以你需要注意你的编码 . 我不是程序员,当然我尽力做到最好,但我想知道我是否在这里做了一些明显的错误,这些错误可以很容易地解决,或者是否会发生(太)经常我必须注意我的朱莉娅编码 - 因为那时我学习它可能不值得 . 同样,如果我能在这里使Julia更快(我非常确定我可以,例如分配看起来有点大),我可能也可以让MATLAB更快 . 我对朱莉娅的希望是 - 对于类似的代码 - 它将比MATLAB运行得更快 .
在对时间进行一些评论后,我还运行了这段代码:
using Dierckx
tic()
const spacing=1.5
const Nxx = 300
const Naa = 350
const Nalal = 200
const sigma = 10
const NoIter = 500
xx=Array(Float64,Nxx)
xmin = 0.01
xmax = 400
xx[1] = xmin
for i=2:Nxx
xx[i] = xx[i-1] + (xmax-xx[i-1])/((Nxx-i+1)^spacing)
end
f_U(c) = c.^(1-sigma)/(1-sigma)
W=Array(Float64,Nxx,1)
W[:,1] = f_U(xx)
W_temp = Array(Float64,Nalal,Naa)
interp_spline = Spline1D(xx,W[:,1])
xprime = ones(Nalal,Naa)
for i=1:NoIter
for j=1:Naa
for k=1:Nalal
W_temp[k,j] = evaluate(interp_spline, xprime[k,j])
end
end
end
toc()
elapsed time:
7.336371495 seconds
甚至慢得多,嗯......
另一个编辑:在这种情况下,消除一个循环实际上使它更快,但仍然无法与MATLAB相比 . 代码:
function performance2()
const spacing=1.5
const Nxx = 300
const Naa = 350
const Nalal = 200
const sigma = 10
const NoIter = 500
xx=Array(Float64,Nxx)
xmin = 0.01
xmax = 400
xx[1] = xmin
for i=2:Nxx
xx[i] = xx[i-1] + (xmax-xx[i-1])/((Nxx-i+1)^spacing)
end
f_U(c) = c.^(1-sigma)/(1-sigma)
W=Array(Float64,Nxx,1)
W[:,1] = f_U(xx)
W_temp = Array(Float64,Nalal,Naa)
interp_spline = Spline1D(xx,W[:,1])
xprime = ones(Nalal,Naa)
for i=1:NoIter
for j=1:Naa
W_temp[:,j] = evaluate(interp_spline, xprime[:,j])
end
end
end
@time(performance2())
1.573347 seconds (700.04 k allocations: 620.643 MB, 1.08% gc time)
另一个编辑,现在通过循环迭代相同的次数:
function performance3()
const spacing=1.5
const Nxx = 300
const Naa = 350
const Nalal = 200
const sigma = 10
const NoIter = 500
xx=Array(Float64,Nxx)
xmin = 0.01
xmax = 400
xx[1] = xmin
for i=2:Nxx
xx[i] = xx[i-1] + (xmax-xx[i-1])/((Nxx-i+1)^spacing)
end
f_U(c) = c.^(1-sigma)/(1-sigma)
W=Array(Float64,Nxx,1)
W[:,1] = f_U(xx)
W_temp = Array(Float64,Nalal,Naa)
W_tempr = Array(Float64, Nalal*Naa)
interp_spline = Spline1D(xx,W[:,1])
xprime = ones(Nalal,Naa)
xprimer = reshape(xprime, Nalal*Naa)
for i=1:NoIter
W_tempr = evaluate(interp_spline, xprimer)
end
W_temp = reshape(W_tempr, Nalal, Naa)
end
tic()
performance3()
toc()
elapsed time:
1.480349334 seconds
尽管如此,与MATLAB完全不同 . 顺便说一句,在我的实际问题中,我运行循环轻松50k次,我正在访问更大的xprime矩阵,虽然不确定该部分是否有所作为 .
1 回答
因为我也在学习Julia,我已经尝试过加速OP的代码(对于我的练习!) . 似乎瓶颈本质上是底层的Fortran代码 . 为了验证这一点,我首先将OP的代码重写为最小形式,如下所示:
其中问题的大小保持不变,而输入的x和y坐标被改变,因此结果很简单(0.5) . 在我的机器上,结果是
从现在开始,为了简洁,我将省略[1:3](我已经确认在所有情况下获得的y [1:3]都是正确的) . 如果我们用
copy!(y,x)
替换evaluate()
,结果就变成了所以基本上所有的时间都花在
evaluate()
上 . 现在看一下这个例程的original code,我们看到它在Fortran中调用了splev(),后者又调用fpbspl()(两者都来自Netlib) . 这些例程相当陈旧(写于〜1990年)并且对于当前的计算机似乎没有很好地优化(例如,有许多IF分支和矢量化可能很难...) . 因为代码不是很简单,所以我尝试用OpenMP进行并行化 . 修改后的splev()
就像这样,输入点简单地分为线程:现在用
gfortran -fopenmp
重建包并在上面调用perf()
给出所以缩放看起来很简单(但是如果我以这种方式使用OpenMP会犯一个大错误,请告诉我......)如果上面的结果是正确的,那就意味着需要8个线程才能使用这个Fortran代码来匹配OP 's machine. But the good news is that there are probably room for improvement of the Fortran codes (even for serial run). Anyway, the OP'程序的速度's machine. But the good news is that there are probably room for improvement of the Fortran codes (even for serial run). Anyway, the OP'(如最终形式)似乎是在比较底层插值程序的性能,即Matlab中的
interp1()
与Fortran中的splev()
.