在使用分析器加速代码时,我注意到单个数组元素上的标量操作比整个数组上的矢量化操作花费的时间更长。显然,这不是人们所期望的,因为在使用数组元素时只有一个操作发生,但是在数组上进行矢量化操作时会进行许多操作(尽管是矢量化的)。
我看到这个的上下文有点复杂,标量操作没有在与数组相同的嵌套对象上完成。但是,我能用脚本复制这种奇怪的东西:
%%%%%%%%%%%%%
%% 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命令导致的反直觉加速,似乎不受脚本和类方法之间的编译差异的影响。
总之,
对数组元素的标量操作似乎比数组操作更难以运行。从数组中提取单个元素可能存在某种速度损失,我不知道。
post-hoc 清除莫名其妙地加速了标量操作,所以 它比阵列操作更快。无论是否存在明确命令,这都是人们所期望的。
这些观察结果似乎不受脚本和类方法之间的任何编译差异的影响。
如果有人能够对可能导致上述观察的内部工作有所了解,也许我会利用这种洞察力来消除我的类方法中各个数组元素的标量操作的缓慢。
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。
答案 0 :(得分:0)
答案 1 :(得分:-1)
不确定哪个是真正的原因,但我会调查三件事:
编辑:JIT - &gt;循环优化