【问题标题】:How to nest multiple parfor loops如何嵌套多个 parfor 循环
【发布时间】:2013-11-30 01:43:27
【问题描述】:

parfor 是一种在多个“工作人员”之间分配独立迭代的密集计算的便捷方式。一个有意义的限制是 parfor-loops 不能嵌套,并且总是可以回答类似问题,例如 therethere

为什么如此需要跨循环边界的并行化

考虑以下一段代码,其中在允许 4 个工作人员的机器上,迭代所花费的时间变化很大。两个循环都迭代了 6 个值,显然很难在 4 个值之间共享。

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

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

组合循环所需的模式

理想情况下,somefun 对所有 rowcol 对都运行,并且无论迭代哪个迭代,工作人员都应该忙碌起来。解决方案应该是这样的

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

不幸的是,即使我知道哪个内置函数创建了一个包含 rowcol 的所有组合的矩阵,MATLAB 也会报错 Parfor 语句的范围必须是行向量。 em> 然而,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 的内置函数是什么?有人想出了一个方便的惯用模式吗?

【问题讨论】:

    标签: matlab nested-loops parfor


    【解决方案1】:

    MrAzzman 已经指出了如何线性化嵌套循环。这是线性化 n 个嵌套循环的一般解决方案。

    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

    【讨论】:

    • 使用 prodind2sub 打包和解包迭代使得这非常整洁。
    • 我发现你所说的方法有些时差,请检查,tic; for a=1:4 for b=1:5 f(a)=sum(a,b); end end toc; tic; iterations=[5,4]; for ix=1:prod(iterations) [b,a]=ind2sub(iterations,ix); f(a)=sum(a,b); end toc;
    • @USER:没错,线性化循环的开销更高,但将它与parfor 和一些更复杂的计算结合起来,这个解决方案通常更快。
    • @Daniel 如果我的循环增量值不是 1 怎么办?像这样: parfor ii = 1 : 10 for j = 1 : 4 : 9 for k = 1 : 0.3 : 2 for l = 5 : 40 : 200
    • @Trmotta:您可以将任何索引转换为整数索引。而不是for j = 1 : 4 : 9,你使用j_list=1 : 4 : 9;for j_index=1:numel(j_list),j=j_list(j_index),end
    【解决方案2】:

    您应该可以使用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);
    

    【讨论】:

    • 谢谢。我喜欢使用上限乘积的第二种选择。它似乎比使用bsxfun 更灵活,例如在一段脚本代码中。
    【解决方案3】:

    根据 @DanielR@MrAzzaman 的回答,我发布了两个函数,iterlin@987654323 @ 代替 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

    【讨论】:

    • 你好,我看过你的函数和他们的例子,你能告诉我你的函数中的parfor需要parpoolmatlabpool打开吗?如何通过tic toc查看其效率
    • 您能否提供一个示例,您将计算输出放入 X{k} 中,用于板、行和列的每种组合?例如,我之后如何访问 row=3、col=4 和 plate=2 的结果?
    猜你喜欢
    • 2016-04-24
    • 2014-06-10
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多