parfor
是在几个“工人”之间分配密集计算的独立迭代的便捷方式。一个有意义的限制是parfor
- 循环不能嵌套,并且总是,这是there和there等类似问题的答案。
为什么跨越循环边界的并行化是如此理想
考虑以下代码,其中迭代在允许4个工作者的机器上占用大量可变时间。两个循环迭代超过6个值,显然难以在4个中共享。
for row = 1:6
parfor col = 1:6
somefun(row, col);
end
end
为parfor
选择内循环似乎是一个好主意,因为对somefun
的单个调用比外循环的迭代更可变。但是如果每次调用somefun
的运行时间非常相似怎么办?如果运行时有趋势并且我们有三个嵌套循环怎么办?这些问题经常出现,人们会前往extremes。
合并循环所需的模式
理想情况下,对somefun
和row
的所有对运行col
,工作人员应该忙碌而不管哪个迭代变换。解决方案应该看起来像
parfor p = allpairs(1:6, 1:6)
somefun(p(1), p(2));
end
不幸的是,即使我知道哪个内置函数创建了一个包含row
和col
的所有组合的矩阵,MATLAB也会抱怨错误 parfor语句的范围必须是一行向量。然而,for
不会抱怨并且很好地迭代列。一个简单的解决方法是创建该矩阵,然后使用parfor
索引它:
p = allpairs(1:6, 1:6);
parfor k = 1:size(pairs, 2)
row = p(k, 1);
col = p(k, 2);
somefun(row, col);
end
我正在寻找的内置函数代替allpairs
是什么?有人提出了一个方便的惯用模式吗?
答案 0 :(得分:9)
MrAzzman已经指出如何线性化嵌套循环。这是线性化嵌套循环的一般解决方案。
1)假设你有一个简单的嵌套循环结构:
%dummy function for demonstration purposes
f=@(a,b,c)([a,b,c]);
%three loops
X=cell(4,5,6);
for a=1:size(X,1);
for b=1:size(X,2);
for c=1:size(X,3);
X{a,b,c}=f(a,b,c);
end
end
end
2)使用for循环的基本线性化:
%linearized conventional loop
X=cell(4,5,6);
iterations=size(X);
for ix=1:prod(iterations)
[a,b,c]=ind2sub(iterations,ix);
X{a,b,c}=f(a,b,c);
end
3)使用parfor循环进行线性化。
%linearized parfor loop
X=cell(4,5,6);
iterations=size(X);
parfor ix=1:prod(iterations)
[a,b,c]=ind2sub(iterations,ix);
X{ix}=f(a,b,c);
end
4)使用具有传统for循环的第二个版本,改变执行迭代的顺序。如果有什么依赖于此,你必须颠倒索引的顺序。
%linearized conventional loop
X=cell(4,5,6);
iterations=fliplr(size(X));
for ix=1:prod(iterations)
[c,b,a]=ind2sub(iterations,ix);
X{a,b,c}=f(a,b,c);
end
使用parfor
循环时撤消顺序无关紧要。你根本不能依赖执行的顺序。如果您认为它有所作为,则无法使用parfor
。
答案 1 :(得分:5)
您应该可以使用bsxfun
执行此操作。我相信 bsxfun
会尽可能并行化代码(有关详细信息,请参阅here),在这种情况下,您应该能够执行以下操作:
bsxfun(@somefun,(1:6)',1:6);
你可能想要对此进行基准测试。
或者,您可以执行以下操作:
function parfor_allpairs(fun, num_rows, num_cols)
parfor i=1:(num_rows*num_cols)
fun(mod(i-1,num_rows)+1,floor(i/num_cols)+1);
end
然后致电:
parfor_allpairs(@somefun,6,6);
答案 2 :(得分:2)
基于 @DanielR 和 @MrAzzaman 的答案,我发布了两个函数, iterlin
和 iterget
代替prod
和ind2sub
,允许迭代范围,如果那些不是从一个开始的话。模式的示例变为
rng = [1, 4; 2, 7; 3, 10];
parfor k = iterlin(rng)
[plate, row, col] = iterget(rng, k);
% time-consuming computations here %
end
该脚本将处理板1至4中的第2至7行和第3至10列的井,而没有任何工人闲置,同时有更多的井等待处理。为了帮助某人,我将iterlin
和iterget
存放在MATLAB File Exchange。