Arrays Matlab标量运算比数组运算耗时更长

Arrays Matlab标量运算比数组运算耗时更长,arrays,performance,matlab,Arrays,Performance,Matlab,在使用探查器加速代码时,我注意到单个数组元素上的标量操作比整个数组上的矢量化操作花费的时间更长。显然,这并不是人们所期望的,因为在处理数组元素时,只有一个操作发生,而在对数组进行矢量化操作时,会发生许多操作(尽管是矢量化的) 我看到这一点的上下文有点复杂,标量操作没有在与数组相同的嵌套对象上执行。然而,我能够用一个脚本复制这种奇怪的现象: %%%%%%%%%%%%% %% tst1.m %%%%%%%%%%%%% % Generate random data for ix=1:5; for

在使用探查器加速代码时,我注意到单个数组元素上的标量操作比整个数组上的矢量化操作花费的时间更长。显然,这并不是人们所期望的,因为在处理数组元素时,只有一个操作发生,而在对数组进行矢量化操作时,会发生许多操作(尽管是矢量化的)

我看到这一点的上下文有点复杂,标量操作没有在与数组相同的嵌套对象上执行。然而,我能够用一个脚本复制这种奇怪的现象:

%%%%%%%%%%%%%
%%  tst1.m
%%%%%%%%%%%%%

% Generate random data
for ix=1:5; for iy=1:5
   x(ix).y(iy).z=rand(1,10);
end; end
Ntest=1e7;

disp('Script tst#1a: Operation on one array element:')
tic
for i=1:Ntest
   a=0.5>x(3).y(3).z(1);
end % for i
toc

% clear a

disp('Script tst#1b: Vectorized operation on entire array:')
tic
for i=1:Ntest
   a=0.5>x(3).y(3).z;
end % for i
toc
上面的脚本tst#1a是单个数组元素操作,而脚本tst#1b是整个数组上的矢量化操作。结果是:

Script tst#1a: Operation on one array element:
Elapsed time is 6.260495 seconds.
Script tst#1b: Vectorized operation on entire array:
Elapsed time is 4.491822 seconds.
Script tst#1a: Operation on one array element:
Elapsed time is 5.888381 seconds.
Script tst#1b: Vectorized operation on entire array:
Elapsed time is 4.636491 seconds.

Script tst#2a: Operation on one array element:
Elapsed time is 3.435526 seconds.
Clearing a
Script tst#2b: Vectorized operation on entire array:
Elapsed time is 4.531256 seconds.


Method tst#1a: Operation on one array element:
Elapsed time is 5.732293 seconds.
Method tst#1b: Vectorized operation on entire array:
Elapsed time is 4.550085 seconds.

Method tst#2a: Operation on one array element:
Elapsed time is 3.266772 seconds.
Clearing a
Method tst#2b: Vectorized operation on entire array:
Elapsed time is 4.664736 seconds.
可以看出,标量操作需要更长的时间。有人能猜出这种违反直觉的观察的原因吗?也许测试代码中有一些非常愚蠢的东西

在组装上述测试时,我还发现,如果清除左侧变量(如上面注释的语句),标量操作的速度几乎提高了2倍。我不知道确切的原因,但不管是什么原因,我发现更奇怪的是,即使清除发生在标量操作测试代码之后,标量操作测试代码也会加速。以下是未注释clear命令的同一个m文件:

%%%%%%%%%%%%%
%%  tst2.m
%%%%%%%%%%%%%

% Generate random data
for ix=1:5; for iy=1:5
   x(ix).y(iy).z=rand(1,10);
end; end
Ntest=1e7;

disp('Script tst#2a: Operation on one array element:')
tic
for i=1:Ntest
   a=0.5>x(3).y(3).z(1);
end % for i
toc

disp('Clearing a');
clear a

disp('Script tst#2b: Vectorized operation on entire array:')
tic
for i=1:Ntest
   a=0.5>x(3).y(3).z;
end % for i
toc
以下是结果,显示了前面的标量操作测试代码令人费解的加速(与tst1.m的结果相比):

这两个测试都不能完全反映我的情况,因为我使用类方法而不是脚本。我记得在一个论坛上读到,与脚本相比,函数和方法为编译器优化提供了更多的机会。为了弄清楚这是否可以解释标量操作的相对缓慢以及由于事后清除而导致的违反直觉的加速,我将上述两个测试脚本放入类方法中:

%%%%%%%%%%%%%%
%%  cTest.m
%%%%%%%%%%%%%%
classdef cTest < handle
methods

   function tst1(o)

      % Generate random data
      for ix=1:5; for iy=1:5
         x(ix).y(iy).z=rand(1,10);
      end; end
      Ntest=1e7;

      disp('Method tst#1a: Operation on one array element:')
      tic
      for i=1:Ntest
         a=0.5>x(3).y(3).z(1);
      end % for i
      toc

      % clear a
      disp('Method tst#1b: Vectorized operation on entire array:')
      tic
      for i=1:Ntest
         a=0.5>x(3).y(3).z;
      end % for i
      toc

   end % function tst1

   function tst2(o)

      % Generate random data
      for ix=1:5; for iy=1:5
         x(ix).y(iy).z=rand(1,10);
      end; end
      Ntest=1e7;

      disp('Method tst#2a: Operation on one array element:')
      tic
      for i=1:Ntest
         a=0.5>x(3).y(3).z(1);
      end % for i
      toc

      disp('Clearing a');
      clear a

      disp('Method tst#2b: Vectorized operation on entire array:')
      tic
      for i=1:Ntest
         a=0.5>x(3).y(3).z;
      end % for i
      toc

   end % function tst2

end % method
end % classdef
综合结果如下:

Script tst#1a: Operation on one array element:
Elapsed time is 6.260495 seconds.
Script tst#1b: Vectorized operation on entire array:
Elapsed time is 4.491822 seconds.
Script tst#1a: Operation on one array element:
Elapsed time is 5.888381 seconds.
Script tst#1b: Vectorized operation on entire array:
Elapsed time is 4.636491 seconds.

Script tst#2a: Operation on one array element:
Elapsed time is 3.435526 seconds.
Clearing a
Script tst#2b: Vectorized operation on entire array:
Elapsed time is 4.531256 seconds.


Method tst#1a: Operation on one array element:
Elapsed time is 5.732293 seconds.
Method tst#1b: Vectorized operation on entire array:
Elapsed time is 4.550085 seconds.

Method tst#2a: Operation on one array element:
Elapsed time is 3.266772 seconds.
Clearing a
Method tst#2b: Vectorized operation on entire array:
Elapsed time is 4.664736 seconds.
在4个输出文本块中,前2个块是上述2个sript测试的重新运行,而最后2个输出块执行相同的代码,但作为类方法。结果是相似的,因此标量操作的莫名其妙的缓慢,以及由于事后清除命令而产生的违反直觉的加速,似乎不会受到脚本和类方法之间编译差异的影响

总之

  • 数组元素上的标量操作运行速度似乎比数组操作慢得令人费解。也许从数组中提取单个元素会带来某种速度损失,我对此一无所知

  • 一个post-hoc clear莫名其妙地加快了标量运算的速度 它比数组操作快。无论是否有明确的命令,这都是人们所期望的

  • 这些观察结果似乎不受脚本和类方法之间编译差异的影响

  • 如果有人能对可能导致上述观察结果的内部工作原理有所了解,或许我可以利用这一见解来消除类方法中单个数组元素上标量操作的缓慢性

    后注:观察#1即使没有在结构阵列的层中深度嵌套阵列也可以看到:

    >> clear all; x=rand(1,10); tic; for i=1:1e7; a=0.5>x(1); end; toc
       Elapsed time is 0.092028 seconds.
    
    >> clear all; x=rand(1,10); tic; for i=1:1e7; a=0.5>x; end; toc
       Elapsed time is 1.344769 seconds.
    

    这是在3Ghz笔记本电脑上使用MATLAB版本8.5.0.197613(R2015a),该笔记本电脑运行64位Windows7,带有8GB RAM,并且没有其他运行方式来消耗内存。Matlab使用的是550GB,Internet Explorer使用的是240GB。

    不确定真正的原因是什么,但我要调查三件事:

    • 处理问题需要时间
    • 索引可能会增加复杂性,因此循环优化不再有效——这是我不久前在循环中的其他体验中观察到的,在一些看似无辜的更改之后,速度突然下降

    编辑:JIT->loop optimization

    Alexander Kemp的答案似乎是最有可能的解释,基于迄今为止的信息。索引到数组中以访问单个元素似乎会带来很大的时间开销。可能不是标量运算本身比矢量化运算耗时更长;正是从标量运算数组中提取元素导致了速度损失。

    我无法用Matlab 2013a确认您对
    tst1.m
    的结果。你用的是什么Matlab版本?@Daniel:谢谢你的代码。我在帖子的末尾添加了版本信息。你得到了什么结果?大概是你得到的
    tst2.m
    的结果非常奇怪。我认为在进入go.m时,我的基本工作区可能会变得杂乱无章,所以我在开始时添加了
    清除所有
    ,我仍然认为单元素操作比数组范围的操作(tst1.m)慢,但使用
    清除a
    (tst2.m)则反之亦然。@Alexander Kepm:你可能知道了什么。每个测试脚本和方法当前仅将数组范围的操作与索引的一个元素操作进行比较。我添加了另一个数组范围的操作,但是可以通过索引来访问该数组:
    0.5>x(3).y(3).z(1:10)
    (1:10)
    索引是冗余的;它只是抓住了所有的z。此“索引”数组范围的操作比不带索引的数组范围的操作或索引的单元素操作慢。我不会发布修改后的代码,因为它很琐碎,可能是一个答案,我现在还不太确定。