为什么Matlab。*运算符在某些情况下比标量更快?

时间:2016-01-16 21:22:10

标签: performance matlab

请考虑以下代码:

a=rand(10000); b=rand(10000);
tic; 2*(a<b); toc;
tic; 2.*(a<b); toc;    

结果是:

Elapsed time is 0.938957 seconds.
Elapsed time is 0.426517 seconds.

为什么第二种情况比第一种情况快两倍?

编辑: 无论你测试它的顺序如何,我都可以用任何大小的矩阵获得相同的结果

(a<b).*3.56 vs (a<b)*3.56
例如

,但不是

(a.*b)*2 vs (a.*b).*2

(a*b)*2 vs (a*b).*2

似乎存在与逻辑数组的链接,因为我与

具有相同的结果
(a&b)*2 vs (a&b).*2

计算机:R2015b,Windows 10 x64

3 个答案:

答案 0 :(得分:6)

我建议对性能进行更严格的检查。将测试放在一个命名函数中,让MATLAB优化两段代码,并多次运行两个代码,选择最快的运行时。我的预感是他们应该花费相同的时间,虽然我现在无法检查合理的矩阵大小。这就是我要做的事情:

function product_timing(N)

a=rand(N);
b=rand(N);

tmin=inf;
for k=1:10
    tic;
    res1=2*(a<b);
    t=toc;

    if t<tmin
        tmin=t;
    end
end

disp(tmin);


tmin=inf;
for k=1:10
    tic;
    res2=2.*(a<b);
    t=toc;

    if t<tmin
        tmin=t;
    end
end

更新

在我的R2012b上,两种方法之间似乎没有明显的区别。然而,正如其他人所指出的那样,R2015b及其新的执行引擎完全不同。

虽然我仍然不确定答案,但我要收集@x1hgg1x(对此答案和问题的评论)和@LuisMendoin chat)的反馈意见,以便详细说明在我的无知上:

  • c*3.56是一个整数因子(线程数?),如果c.*3.56c,则比logical(有任何标量)慢一倍,但如果c没有1}}是uint8double
  • 同样适用于矢量,而不仅仅是方形矩阵

如上所述on a MATLAB product page

  

使用重新设计的MATLAB®执行引擎更快地运行程序。

     

改进的架构使用所有的即时(JIT)编译   具有单个执行路径的MATLAB代码。引擎提供   提高语言质量,为未来提供平台   增强。

     

具体的性能改进包括:

     

...

     

元素智慧数学运算

     

优化了许多元素数学运算的执行。这些   操作是对数组的逐元素算术运算   如下:

     

>> b = ((a+1).*a)./(5-a);

但是,在查看.**的文档时,我看不到有关该问题的太多信息。来自array vs matrix operations的关于数组操作的注释,例如.*

  

如果一个操作数是标量而另一个不是标量,则适用MATLAB   另一个操作数的每个元素的标量。这个属性是   称为标量扩展,因为标量扩展为数组   与其他输入大小相同,然后操作执行   通常使用两个数组。

doc of the matrix product *

  

如果至少有一个输入是标量,那么A * B相当于A. * B和   是可交换的。

正如我们所见,A*BA.*B等价是有争议的。嗯,它们在数学上是等价的,但是有些奇怪的事情正在发生。

由于上面的注释,以及仅logical数组出现性能差异这一事实,我认为这是一个未记录的功能。我认为它与logical只相关,每个只占1个字节,但加速并不会显示uint8数组。我建议,由于logical实际上只包含一位信息,因此可以进行一些内部优化。这仍然无法解释为什么mtimes没有这样做,而且肯定与timesmtimes的内部运作有关。

有一件事是肯定的:times对于标量操作数实际上并没有回归mtimes(也许它应该?)。由于在R2012b中缺少整体效果,我相信上面提到的新执行引擎的优化数组操作分别处理逻辑数组,允许加速scalar.*logical_array的特殊情况,但是缺少相同的优化mtimes

答案 1 :(得分:0)

对于背景,*运算符是矩阵运算符,而*.是元素运算符(请参阅http://www.mathworks.com/help/matlab/matlab_prog/array-vs-matrix-operations.html)。

在您的测试中,a和b是随机的1000x1000矩阵,它们评估为您想要使用这两种方法进行缩放的逻辑1000x1000矩阵。除了让Mathworks的开发人员告诉我们发生了什么之外,我认为我们只能推测导致差异的原因(并回答你的问题)。

因为,我们不应该推测这些答案,我将在这里正式停止。然而,有趣的是你偶然发现了同样的事情。

所以,非正式地,我怀疑你发现了一些额外的开销MATLAB正在实现处理与*运算符的矩阵运算,这些运算符在元素运算符中被短路或旁路。

考虑以下

c = a<b;
tic; d*(c); toc;  % case 1
tick;d.*(c); toc; % case 2

其中ab由上面的代码定义,为了便于解释,d保留为未知值,接下来。

当乘以矩阵时,第一个矩阵中的列数需要与第二个矩阵中的行数匹配。 c将是1000x1000矩阵,对,d要么需要1000列(例如size(d,2)==1000),要么需要是标量。在第二种情况下,d必须是标量(否则将抛出错误)。

此外,在进行乘法时,可能会有一些额外的准备(不多,但有些),因为您可以正确地为最终产品中的每个位置获得总和。在这里,我们知道d==2并且它是一个标量,因此乘法可以在适当的位置完成。但是,我们知道因为我们看到了它。我不认为乘法算法在这里作为一个极端情况 - 看看d是一个标量值。如果是,它将/应该只调用*.例程。而且,也许这就是正在发生的事情,我们只是获得了一些级别的堆栈开销。当然是非官方的。

答案 2 :(得分:-1)

是的,“。*”在2015b上标量和逻辑数组之间的速度更快:

a = rand(10000); b = rand(10000);
timeit(@()2*a)
timeit(@()2.*a)

timeit(@()2.*(a>b))
timeit(@()2*double(a>b))
timeit(@()2*(a>b))