如何对涉及递归乘法和累加的循环进行矢量化?

时间:2015-06-03 16:00:24

标签: matlab parallel-processing vectorization

matlab中,我有一个循环形式:

a=1;
for (i = 1:N)
   a = a * b(i) + c(i);
end

这个循环可以被矢量化,还是部分展开?

1 个答案:

答案 0 :(得分:3)

对于每个bc长度4,当循环展开时,您将拥有 -

output = b1b2b3b4 + c1b2b3b4 + c2b3b4 + c3b4 + c4

因此,通用公式为:

output = b1b2b3...bN + c1b2b3..bN + c2b3..bN + c3b4..bN + ... cN-1bN + cN

b的累积乘积可以用cumprod计算,其元素被翻转或“反转”。休息是关于元素乘法与c元素的比较,这些元素移位1位,然后包括累积积和c中剩余的标量元素,最后将所有这些元素相加以得到最终的标量输出。< / p>

因此,编码版本看起来像这样 -

cumb = cumprod(b,'reverse');
a = sum(cumb(2:end).*c(1:end-1)) + cumb(1) + c(end);

基准

让我们将问题中的循环方法与本文前面提出的矢量化方法进行比较。

以下是作为函数的方法 -

function a = loopy(b,c)
N = numel(b);
a = 1;
for i = 1:N
   a = a * b(i) + c(i);
end
return;

function a = vectorized(b,c)
cumb = cumprod(b,'reverse');
a = sum(cumb(2:end).*c(1:end-1)) + cumb(1) + c(end);
return;

以下是对这两种方法进行基准测试的代码 -

datasizes = 10.^(1:8);
Nd = numel(datasizes);

time_loopy = zeros(1,Nd);
time_vectorized = zeros(1,Nd);
for k1 = 1:numel(datasizes)
    N = datasizes(k1);
    b = rand(1,N);
    c = rand(1,N);

    func1 = @() loopy(b,c);
    func2 = @() vectorized(b,c);
    time_loopy(k1) = timeit(func1);
    time_vectorized(k1) = timeit(func2);
end

figure,
loglog(datasizes,time_loopy,'-rx'), hold on
loglog(datasizes,time_vectorized,'-b+'),    
set(gca,'xgrid','on'),set(gca,'ygrid','on'),
xlabel('Datasize (# elements)'), ylabel('Runtime (s)')
legend({'Loop','Vectorized'}),title('Runtime Plot')

figure,
semilogx(datasizes,time_loopy./time_vectorized,'-k.')
set(gca,'xgrid','on'),set(gca,'ygrid','on'),
xlabel('Datasize (# elements)'), ylabel('Speedup (x)')
legend({'Speedup with vectorized method over loopy one'}),title('Speedup Plot')

这是运行时和加速图 -​​

enter image description here

enter image description here

很少有观察

阶段#1:从开始datasize到1000个元素,loopy方法占上风,因为矢量化方法没有获得足够的元素来从一切一体化的方法中受益。

阶段#2:从1000个元素到1000,0000个元素,矢量化方法似乎更好,因为它可以使用足够的元素。

阶段#3:对于更大的数据大小的情况,似乎存储和使用矢量化方法处理数百万个元素的内存带宽需求,而不是仅使用循环方法的标量值 可能会盯住矢量化方法。

结论:如果性能是最重要的标准,可以使用矢量化方法,也可以使用基于数据的原始循环代码。