在matlab
中,我有一个循环形式:
a=1;
for (i = 1:N)
a = a * b(i) + c(i);
end
这个循环可以被矢量化,还是部分展开?
答案 0 :(得分:3)
对于每个b
和c
长度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')
这是运行时和加速图 -
很少有观察
阶段#1:从开始datasize到1000个元素,loopy方法占上风,因为矢量化方法没有获得足够的元素来从一切一体化的方法中受益。
阶段#2:从1000个元素到1000,0000个元素,矢量化方法似乎更好,因为它可以使用足够的元素。
阶段#3:对于更大的数据大小的情况,似乎存储和使用矢量化方法处理数百万个元素的内存带宽需求,而不是仅使用循环方法的标量值 可能会盯住矢量化方法。
结论:如果性能是最重要的标准,可以使用矢量化方法,也可以使用基于数据的原始循环代码。