Matlab标量操作比数组操作花费的时间更长

时间:2016-01-16 15:34:09

标签: arrays performance matlab

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

我看到这个的上下文有点复杂,标量操作没有在与数组相同的嵌套对象上完成。但是,我能用脚本复制这种奇怪的东西:

%%%%%%%%%%%%%
%%  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.

可以看出,标量操作需要更长的时间。有人能够推测出这种违反直觉的观察的原因吗?也许在测试代码中真的很蠢?

在组装上述测试时,我还发现如果我清除了左侧变量,例如在上面评论的语句中,标量操作加速了近2倍。我不知道'确切地知道为什么,但不管原因如何,我发现标量操作测试代码加速甚至更加奇怪,即使在标量操作测试代码之后发生了清除。这是一个相同的m文件,其中clear命令未注释:

%%%%%%%%%%%%%
%%  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的结果相比):

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

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

%%%%%%%%%%%%%%
%%  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

我使用以下&#34; testbench&#34;比较所有上述m文件的执行情况。脚本:

%%%%%%%%%%%
%%  go.m
%%%%%%%%%%%
clc
c = cTest;

tst1
disp(' ')
tst2

fprintf('\n\n')

c.tst1
disp(' ')
c.tst2

综合结果如下:

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. 对数组元素的标量操作似乎比数组操作更难以运行。从数组中提取单个元素可能存在某种速度损失,我不知道。

  2. post-hoc 清除莫名其妙地加速了标量操作,所以 它比阵列操作更快。无论是否存在明确命令,这都是人们所期望的。

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

  4. 如果有人能够对可能导致上述观察的内部工作有所了解,也许我会利用这种洞察力来消除我的类方法中各个数组元素的标量操作的缓慢。

    AFTERNOTE:即使没有在结构数组的层中深度嵌套数组,也可以看到观察#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位Windows 7,内存为8GB,并且没有其他运行来消耗内存。 Matlab使用的是550GB,Internet Explorer使用的是240GB。

2 个答案:

答案 0 :(得分:0)

根据迄今为止的信息,亚历山大·坎普的回答似乎是可能的解释。索引到数组以访问单个元素似乎带来了大量的时间开销。可能不是标量操作本身需要比矢量化操作更长的时间;它是从数组中提取一个元素,用于标量操作,导致速度惩罚。

答案 1 :(得分:-1)

不确定哪个是真正的原因,但我会调查三件事:

  • 增加成本时间
  • 索引可能会增加复杂性,因此循环优化不再起作用 - 这是我前段时间观察到的其他循环中的事件,在一些看似无辜的变化之后速度突然下降。

编辑:JIT - &gt;循环优化