【问题标题】:Matlab scalar operation takes longer than array operationMatlab标量操作比数组操作花费更长的时间
【发布时间】:2016-04-22 02:10:38
【问题描述】:

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

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

%%%%%%%%%%%%%
%%  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倍。我不知道究竟为什么,但不管是什么原因,我发现更奇怪的是,即使清除发生在标量操作测试代码之后,标量操作测试代码也会加速。这是未注释 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 的结果相比):

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

我使用以下“testbench”脚本比较了上述所有 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 个脚本测试的重新运行,而最后 2 个输出块执行相同的代码,但作为类方法。结果是相似的,因此标量操作的莫名其妙的缓慢,以及由于 post hoc clear 命令导致的违反直觉的加速,似乎不受脚本和类方法之间的编译差异的影响。

总之,

  1. 对数组元素的标量操作似乎莫名其妙地比数组操作慢。也许从数组中提取单个元素会导致某种速度损失,我不知道。

  2. post-hoc clear 莫名其妙地加快了标量运算,因此 它比数组操作更快。无论 clear 命令是否存在,这都是人们所期望的。

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

如果有人能阐明可能导致上述观察结果的内部工作原理,也许我会利用这种洞察力来摆脱在我的类方法中对单个数组元素进行标量操作的缓慢问题。

后注:即使没有在结构数组层中深度嵌套数组,也可以看到观察结果#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.

这是在运行 64 位 Windows 7 和 8GB RAM 的 3Ghz 笔记本电脑上使用 MATLAB 版本 8.5.0.197613 (R2015a),并且没有太多运行以消耗内存。 Matlab 使用 550GB,Internet Explorer 使用 240GB。

【问题讨论】:

  • 我无法使用 Matlab 2013a 确认您对 tst1.m 的结果。你用什么 Matlab 版本?
  • @Daniel:感谢您尝试代码。我在帖子末尾添加了版本信息。你得到了什么结果?
  • 大概是你在tst2.m得到的结果
  • 很奇怪。我认为进入 go.m 时我的基本工作区可能会变得混乱,所以我在开头添加了clear all...我仍然看到单元素操作比数组范围操作(tst1.m)慢但是反之亦然 clear a (tst2.m)。

标签: arrays performance matlab


【解决方案1】:

根据迄今为止的信息,Alexander Kemp 的回答似乎是可能的解释。对数组进行索引以访问单个元素似乎会带来大量的时间开销。可能不是标量 操作 本身比矢量化操作花费更长的时间;导致速度损失的是从数组中提取元素以进行标量运算。

【讨论】:

    【解决方案2】:

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

    • 处理需要时间
    • 索引可能会增加复杂性,因此循环优化不再起作用 - 这是我前段时间观察到的循环中的其他表达式,在一些看似无害的更改后速度突然下降。

    已编辑:JIT -> 循环优化

    【讨论】:

    • @Alexander Kepm:您可能正在做某事。当前,每个测试脚本和方法仅将数组范围的操作与索引的单元素操作进行比较。我添加了另一个数组范围的操作,但数组是通过索引访问的:0.5 &gt; x(3).y(3).z(1:10)(1:10) 索引是多余的;它只是抓住了所有的z。这种“索引”数组范围的操作比没有索引的数组范围操作或索引的单元素操作要慢。我不会发布修改后的代码,因为它是微不足道的,并且可能构成一个答案,我目前还不太确定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-26
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    相关资源
    最近更新 更多