删除不需要的feval调用
最近我读到一篇文章关于加速函数实验后的博客。我将利用这个机会,使用版本2020a,使用更新的分析器来调查性能,特别是函数的性能函数宏指令.
内容
代码-尝试1
我有一个简单的文件来计算一个函数在一些点上的值,这样这些值可以在以后使用,也许用于绘图。这是代码。
函数[ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,Nsteps,lims)为绘图准备函数值的原始辅助函数%%的例子:% fh = @(x)平方根(abs(sin(x.^2+17)));% [ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,50000,[0, pi]);% plot(ptsToPlot, vals)%参数Fh (1,1) function_handle;Nsteps (1,1) double = 100000;linms (1,2) double = [-pi, pi];结束Sm = min(lim);Lg = max(lims);% Nsteps+1 =要创建的点数。1/N有助于确定步长。100000比我们需要的多很多,但是我想做一堆%有意义的计算,所以我们得到一个合理的读数。获取要绘制的点的x轴。ptsToPlot = (sm:((lg-sm)/Nsteps):lg)';在循环中使用feval计算输出。为:长度(ptsToPlot) vals(ind,1) = feval(fh, ptsToPlot(ind));结束
我正在使用Home工具条上的运行和时间功能来测量我的时间。
我将调用这个函数的所有变量,使用相同的输入-公平的公平!首先我要设置参数。
Nsteps = 50000;Lims = [0 pi];
然后在剖析器右侧的run And Time小部件左侧的文本框中运行以下代码:
[ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,Nsteps,lims);
这是我们之后看到的
用时0.034秒。否则,这还不是很有用,所以我在顶部的火焰图中单击函数的名称,看看文件中发生了什么。
它显示我正在拨打27号线函数宏指令,很多次。进一步向下滚动,我从Code Analyzer结果中获得了更多信息。
它注意到我没有预先分配输出向量,尽管我似乎应该知道它的大小。
题外话:注意参数块进行错误检查。
代码-尝试2
接下来,我们将看到当我为输出预分配向量时会发生什么。
函数[ptsToPlot, vals] =...myCrudePlotHelperPreAllocLoopFeval (fh Nsteps lims)为绘图准备函数值的原始辅助函数%%的例子:% fh = @(x)平方根(abs(sin(x.^2+17)));% [ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,50000,[0, pi]);% plot(ptsToPlot, vals)%参数Fh (1,1) function_handle;Nsteps (1,1) double = 100000;linms (1,2) double = [-pi, pi];结束Sm = min(lim);Lg = max(lims);% Nsteps+1 =要创建的点数。1/N有助于确定步长。100000比我们需要的多很多,但是我想做一堆%有意义的计算,所以我们得到一个合理的读数。获取要绘制的点的x轴。ptsToPlot = (sm:((lg-sm)/Nsteps):lg)';在循环中使用feval计算输出。vals =零(大小(ptsToPlot));为:长度(ptsToPlot) vals(ind,1) = feval(fh, ptsToPlot(ind));结束
时间缩短到0.026秒。
代码-尝试3
接下来,我想完全失去循环,看看会发生什么。
函数[ptsToPlot, vals] = myCrudePlotHelperFeval(fh,Nsteps,lims)为绘图准备函数值的原始辅助函数%%%的例子:% fh = @(x)平方根(abs(sin(x.^2+17)));% [ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,50000,[0, pi]);% plot(ptsToPlot, vals)%参数Fh (1,1) function_handle;Nsteps (1,1) double = 100000;linms (1,2) double = [-pi, pi];结束%检查输入参数-应该是1或2。其次应该是实数% 2元素向量。如果Nargin < 1错误(“输入参数不够。”)elseifNargin == 1 lims = [-pi, pi];其他的如果长度(lims) ~= 2 || ~isreal(lims)错误(“第二个输入必须是2元实向量。”)结束结束Sm = min(lim);Lg = max(lims);% N+1 =要创建的点数。1/N有助于确定步长。100000比我们需要的多很多,但是我想做一堆%有意义的计算,所以我们得到一个合理的读数。N = 100000;获取要绘制的点的x轴。ptsToPlot = (sm:((lg-sm)/N):lg)';%做一个向量化的feeval,和一个没有feeval的向量化评估。vals = feval(fh, ptsToPlot);
时间缩短到0.015秒。调用函数一次而不是Nsteps多次是值得的!
代码-尝试4
现在我要删除对的调用函数宏指令完全是因为在很长一段时间里调用函数句柄都不需要它了。
函数[ptsToPlot, vals] = myCrudePlotHelperNoFeval(fh,Nsteps,lims)为绘图准备函数值的原始辅助函数%%的例子:% fh = @(x)平方根(abs(sin(x.^2+17)));% [ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,50000,[0, pi]);% plot(ptsToPlot, vals)%参数Fh (1,1) function_handle;Nsteps (1,1) double = 100000;linms (1,2) double = [-pi, pi];结束Sm = min(lim);Lg = max(lims);% Nsteps+1 =要创建的点数。1/N有助于确定步长。100000比我们需要的多很多,但是我想做一堆%有意义的计算,所以我们得到一个合理的读数。获取要绘制的点的x轴。ptsToPlot = (sm:((lg-sm)/Nsteps):lg)';计算向量化评估没有feval。vals = fh(ptsToPlot);
我们现在将时间减少到0.013秒——与其他带有预分配的版本相比并不明显。但比之前的好多了。
讨论
我关心管理费用的原因是函数宏指令当我们做一些自适应或迭代的计算时。在这种情况下,我们无法在一次调用中完成所有的计算。所以我试图从基本计算中挤出尽可能多的性能,这样额外的不必要的东西就没有机会占主导地位。
这是我最后的比较,在循环中有一个预分配的变量,没有函数宏指令.
函数[ptsToPlot, vals] =...myCrudePlotHelperPreAllocLoopFeval (fh Nsteps lims)为绘图准备函数值的原始辅助函数%%的例子:% fh = @(x)平方根(abs(sin(x.^2+17)));% [ptsToPlot, vals] = myCrudePlotHelperLoopFeval(fh,50000,[0, pi]);% plot(ptsToPlot, vals)%参数Fh (1,1) function_handle;Nsteps (1,1) double = 100000;linms (1,2) double = [-pi, pi];结束Sm = min(lim);Lg = max(lims);% Nsteps+1 =要创建的点数。1/N有助于确定步长。100000比我们需要的多很多,但是我想做一堆%有意义的计算,所以我们得到一个合理的读数。获取要绘制的点的x轴。ptsToPlot = (sm:((lg-sm)/Nsteps):lg)';在循环中使用feval计算输出。vals =零(大小(ptsToPlot));为val (ind,1) = fh(ptsToPlot(ind));结束
时间在中间,0.024秒
所以矢量化带来了很多好处,但也有一些好处,可能是值得的,因为去掉了开销函数宏指令.
这会对代码中的一些计算瓶颈产生影响吗?让我们知道在这里.
评论
如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。