如何嵌套多个parfor循环

时间:2013-11-30 01:43:27

标签: matlab nested-loops parfor

parfor是在几个“工人”之间分配密集计算的独立迭代的便捷方式。一个有意义的限制是parfor - 循环不能嵌套,并且总是,这是therethere等类似问题的答案。

为什么跨越循环边界的并行化是如此理想

考虑以下代码,其中迭代在允许4个工作者的机器上占用大量可变时间。两个循环迭代超过6个值,显然难以在4个中共享。

for row = 1:6
    parfor col = 1:6
        somefun(row, col);
    end
end

parfor选择内循环似乎是一个好主意,因为对somefun的单个调用比外循环的迭代更可变。但是如果每次调用somefun的运行时间非常相似怎么办?如果运行时有趋势并且我们有三个嵌套循环怎么办?这些问题经常出现,人们会前往extremes

合并循环所需的模式

理想情况下,对somefunrow的所有对运行col,工作人员应该忙碌而不管哪个迭代变换。解决方案应该看起来像

parfor p = allpairs(1:6, 1:6)
    somefun(p(1), p(2));
end

不幸的是,即使我知道哪个内置函数创建了一个包含rowcol的所有组合的矩阵,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是什么?有人提出了一个方便的惯用模式吗?

3 个答案:

答案 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 代替prodind2sub,允许迭代范围,如果那些不是从一个开始的话。模式的示例变为

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列的井,而没有任何工人闲置,同时有更多的井等待处理。为了帮助某人,我将iterliniterget存放在MATLAB File Exchange