【发布时间】: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 命令导致的违反直觉的加速,似乎不受脚本和类方法之间的编译差异的影响。
总之,
对数组元素的标量操作似乎莫名其妙地比数组操作慢。也许从数组中提取单个元素会导致某种速度损失,我不知道。
post-hoc clear 莫名其妙地加快了标量运算,因此 它比数组操作更快。无论 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.
这是在运行 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