我有一个循环,我使用ppval来评估分段多项式样条曲线的一组值。插值很容易成为循环中最耗时的部分,我正在寻找一种提高函数效率的方法。
更具体地说,我使用有限差分方案来计算摩擦焊缝中的瞬态温度分布。为此,我需要在每个时间步骤重新计算材料属性(作为温度和位置的函数)。速率限制因素是这些值的插值。我可以使用一个替代的有限差分方案(在时域中限制较少),但如果可能的话,我宁愿坚持我所拥有的。
我在下面列出了一个MWE:
x=0:.1:10;
y=sin(x);
pp=spline(x,y);
tic
for n=1:10000
x_int=10*rand(1000,1);
y_int=ppval(pp,x_int);
end
toc
plot(x,y,x_int,y_int,'*') % plot for sanity of data
Elapsed time is 1.265442 seconds.
编辑 - 我应该提一下,我会对值之间的简单线性插值感到满意,但interp1函数比ppval慢
x=0:.1:10;
y=sin(x);
tic
for n=1:10000
x_int=10*rand(1000,1);
y_int=interp1(x,y,x_int,'linear');
end
toc
plot(x,y,x_int,y_int,'*') % plot for sanity of data
Elapsed time is 1.957256 seconds.
答案 0 :(得分:2)
有点令人惊讶interp1
比ppval
慢,但快速查看其源代码,似乎必须检查许多特殊情况并且必须遍历所有因为它不能确定步长是否恒定。
我没有检查时间,但是如果你可以保证表中x的步长是恒定的,并且要插值的值在给定的范围内很明显,我猜你可以加快线性插值的速度。范围,这样您就不必进行任何检查。在这种情况下,线性插值可以转换为简单的查找问题,如下所示:
%data to be interpolated, on grid with constant step
x = 0:0.5:10;
y = sin(x);
x_int = 0:0.1:9.9;
%make sure it is interpolation, not extrapolation
assert(all(x(1) <= x_int & x_int < x(end)));
% compute mapping, this can be precomputed for constant grid
slope = (length(x) - 1) / (x(end) - x(1));
offset = 1 - slope*x(1);
%map x_int to interval 1..lenght(i)
xmapped = offset + slope * x_int;
ind = floor(xmapped);
frac = xmapped - ind;
%interpolate by taking weighted sum of neighbouring points
y_int = y(ind) .* (1 - frac) + y(ind+1) .* frac;
% make plot to check correctness
plot(x, y, 'o-', x_int, y_int, '.')
答案 1 :(得分:2)
这很慢,因为你遇到了JIT的最烦人的限制。这是在SOLAB上的MATLAB标签中有很多这么多问题的原因:
MATLAB的JIT加速器无法加速调用非内置函数的循环。
内置ppval
和interp1
而非(请与type ppval
或edit interp1
核对)。它们的实现并不是特别慢,它们放在循环中时速度不快。
现在我的印象是它在更新版本的MATLAB中越来越好,但“内联”和“非内联”循环之间仍存在相当大的差异。为什么他们的JIT不会通过简单地递归到非内置函数来自动化这个任务,我真的不知道。
无论如何,为了解决这个问题,你应该将ppval
中发生的事情的本质复制粘贴到循环体中:
% Example data
x = 0:.1:10;
y = sin(x);
pp = spline(x,y);
% Your original version
tic
for n = 1:10000
x_int = 10*rand(1000,1);
y_int = ppval(pp, x_int);
end
toc
% "inlined" version
tic
br = pp.breaks.';
cf = pp.coefs;
for n = 1:10000
x_int = 10*rand(1000,1);
[~, inds] = histc(x_int, [-inf; br(2:end-1); +inf]);
x_shf = x_int - br(inds);
zero = ones(size(x_shf));
one = x_shf;
two = one .* x_shf;
three = two .* x_shf;
y_int = sum( [three two one zero] .* cf(inds,:), 2);
end
toc
Profiler:
我糟糕的机器上的结果:
Elapsed time is 2.764317 seconds. % ppval
Elapsed time is 1.695324 seconds. % "inlined" version
差异实际上小于我的预期,但我认为这主要是由于sum()
- 对于这个ppval
案例,我通常只需要评估一个单个每次迭代的网站,你可以不用histc
(但使用简单的矢量化代码)和矩阵/向量乘法x*y
(BLAS)而不是sum(x.*y)
(快速,但不是BLAS-)快)。
哦,好吧,减少约60%也不错:)