似乎 Mathworks 在其幂函数中特殊处理了平方(不幸的是,它是所有内置封闭源代码,我们无法看到)。在我测试 R2013b 时,似乎“.^”、“power”和“realpow”使用相同的算法。对于平方,我相信他们特殊处理为“x.*x”。
1.0x (4.4ms): @()x.^2
1.0x (4.4ms): @()power(x,2)
1.0x (4.5ms): @()x.*x
1.0x (4.5ms): @()realpow(x,2)
6.1x (27.1ms): @()exp(2*log(x))
对于立方体,情况就不同了。它们不再是特例。同样地,
.^
、
power
和
realpow
都是相似的,但这一次要慢得多:
1.0x (4.5ms): @()x.*x.*x
1.0x (4.6ms): @()x.*x.^2
5.9x (26.9ms): @()exp(3*log(x))
13.8x (62.3ms): @()power(x,3)
14.0x (63.2ms): @()x.^3
14.1x (63.7ms): @()realpow(x,3)
让我们跳到16次方,看看这些算法如何扩展:
1.0x (8.1ms): @()x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x
2.2x (17.4ms): @()x.^2.^2.^2.^2
3.5x (27.9ms): @()exp(16*log(x))
7.9x (63.8ms): @()power(x,16)
7.9x (63.9ms): @()realpow(x,16)
8.3x (66.9ms): @()x.^16
所以:
.^
,
power
和
realpow
在指数方面都以恒定时间运行,除非它被特殊处理(-1似乎也被特殊处理)。使用
exp(n*log(x))
技巧在指数方面也是恒定时间,并且更快。唯一我不太理解的结果是重复平方比乘法慢。
如预期,将x
的大小增加100倍会类似地增加所有算法的时间。
那么,故事的寓意是什么呢?当使用标量整数指数时,一定要自己进行乘法运算。在power
及其相关函数中有很多智能功能(指数可以是浮点数、向量等),唯一的例外是Mathworks已经为您优化了的情况。在2013b中,它似乎是x^2
和x^(-1)
。希望随着时间的推移,他们会增加更多的内容。但总的来说,指数运算很难,乘法很容易。在性能敏感的代码中,我认为始终输入x.*x.*x.*x
是没有错的。(当然,在您的情况下,请遵循Luis的建议并利用每个项内部的中间结果!)
function powerTest(x)
f{1} = @() x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x;
f{2} = @() x.^2.^2.^2.^2;
f{3} = @() exp(16.*log(x));
f{4} = @() x.^16;
f{5} = @() power(x,16);
f{6} = @() realpow(x,16);
for i = 1:length(f)
t(i) = timeit(f{i});
end
[t,idxs] = sort(t);
fcns = f(idxs);
for i = 1:length(fcns)
fprintf('%.1fx (%.1fms):\t%s\n',t(i)/t(1),t(i)*1e3,func2str(fcns{i}));
end
x.*x.*x.*x
可能进行的优化表现出奇怪的行为。我尝试了x.*.x.* ... .*x
,其中 "x" 的数量从 2 到 8 不等,时间增加得多少不一。我本来期望会有波动;例如 "8" 情况(=>x.^2.^2.^2
:三个幂运算)应该比 "7"(=> 更多的幂运算)花费更少的时间。 - Luis Mendox.^2*x.^2*x.^2.*x
,这不会比8的x.^2*x.^2*x.^2.*x.^2
慢。如果用这种方式做8比做7更快,Mathworks可能已经在幂函数中实现了这种优化。 - Dennis Jaheruddin