矢量化什么时候是比循环更好或更差的解决方案?

When does vectorization is a better or worse solution than a loop?

在 Matlab 中,我正在尝试矢量化我的代码以缩短模拟时间。然而,我得到的结果是我降低了整体效率。

为了理解这个现象,我创建了 3 个不同的函数,它们做同样的事情但方法不同:

主文件:

clc,
clear,

n = 10000;
Value = cumsum(ones(1,n));

NbLoop = 10000;

time01 = zeros(1,NbLoop);
time02 = zeros(1,NbLoop);
time03 = zeros(1,NbLoop);

for test = 1 : NbLoop

    tic
    vector1 =  function01(n,Value);
    time01(test) = toc ;

    tic
    vector2 =  function02(n,Value);
    time02(test) = toc ;

    tic
    vector3 =  function03(n,Value);
    time03(test) = toc ; 

end

figure(1)
hold on

plot( time01, 'b')
plot( time02, 'g')
plot( time03, 'r')

函数 01:

function vector =  function01(n,Value)

vector = zeros( 2*n,1);
for k = 1:n
    vector(2*k -1) =  Value(k);
    vector(2*k) =  Value(k);
end

end

函数 02:

function vector =  function02(n,Value)

vector = zeros( 2*n,1);
vector(1:2:2*n) = Value; 
vector(2:2:2*n) = Value; 

end

函数 03:

function vector =  function03(n,Value)

MatrixTmp = transpose([Value(:), Value(:)]);
vector = MatrixTmp (:);

end

蓝色图对应于 for - 循环。

n = 100:

n = 10000:

当我运行带有n = 100的代码时,更有效的解决方案是第一个带有for循环的函数。 当 n = 10000 第一个函数变得效率较低。

使用 MATLAB Online 我看到了不同的东西:

n            10000       100
function01   5.6248e-05  2.2246e-06
function02   1.7748e-05  1.9491e-06
function03   2.7748e-05  1.2278e-06
function04   1.1056e-05  7.3390e-07  (my version, see below)

因此,循环版本总是最慢的。方法 #2 对于非常大的矩阵更快,方法 #3 对于非常小的矩阵更快。

原因是方法#3 制作了 2 个数据副本(transpose 或一个矩阵产生了一个副本),如果数据很多,那就不好了。方法#2 使用索引,这很昂贵,但不如将大量数据复制两次那么昂贵。

我建议改为使用此函数(方法 #4),它仅转置向量(本质上是免费的)。这是对方法#3 的简单修改:

function vector = function04(n,Value)
vector = [Value(:).'; Value(:).'];
vector = vector(:);
end

Do you have a way to know how and when to properly replace a for-loop by a vectorised counterpart?

一般来说,矢量化代码总是更快如果没有大的中间矩阵。对于小数据,您可以更积极地进行向量化,对于大数据,有时由于内存压力降低,循环更有效。这取决于矢量化需要什么。

What is the impact of index searching with array of tremendous dimensions?

这里指的是d = data(data==0)等操作。就像其他一切一样,这对小数据很有效,而对大数据则不太有效,因为 data==0 是一个与 data.

大小相同的中间数组

Does Matlab compute in a different manner an array of dimensions 3 or higher than a array of dimension 1 or 2?

不,一般情况下不会。 sum 等函数以与维度无关的方式实现需要引用.

Is there a clever way to replace a while loop that use the result of an iteration for the next iteration?

这在很大程度上取决于操作是什么。 cumsum 等函数通常可用于向量化此类代码,但并非总是如此。


这是我的计时代码,希望能说明如何正确使用timeit:

%n = 10000;
n = 100;
Value = cumsum(ones(1,n));

vector1 = function01(n,Value);
vector2 = function02(n,Value);
vector3 = function03(n,Value);
vector4 = function04(n,Value);
assert(isequal(vector1,vector2))
assert(isequal(vector1,vector3))
assert(isequal(vector1,vector4))

timeit(@()function01(n,Value))
timeit(@()function02(n,Value))
timeit(@()function03(n,Value))
timeit(@()function04(n,Value))

function vector = function01(n,Value)
vector = zeros(2*n,1);
for k = 1:n
    vector(2*k-1) = Value(k);
    vector(2*k) = Value(k);
end
end

function vector = function02(n,Value)
vector = zeros(2*n,1);
vector(1:2:2*n) = Value; 
vector(2:2:2*n) = Value; 
end

function vector = function03(n,Value)
MatrixTmp = transpose([Value(:), Value(:)]);
vector = MatrixTmp(:);
end

function vector = function04(n,Value)
vector = [Value(:).'; Value(:).'];
vector = vector(:);
end