首页 文章

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

提问于
浏览
0

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

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

%%%%%%%%%%%%%
%%  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个sript测试的重新运行,而最后2个输出块执行相同的代码,但是作为类方法 . 结果是类似的,因此标量操作的无法解释的缓慢以及由于事后清除命令导致的违反直觉的加速似乎不受脚本和类方法之间的编译差异的影响 .

综上所述,

  • 对数组元素的标量操作似乎莫名其妙地比数组操作运行得慢 . 从数组中提取单个元素可能存在某种速度损失,我不知道 .

  • 事后清除莫名其妙地加速了标量操作,因此它比阵列操作更快 . 无论是否存在clear命令,这都是人们所期望的 .

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

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

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.

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

2 回答

  • 0

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

    • 压低成本时间

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

    编辑:JIT - >循环优化

  • -1

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

相关问题