Как вложить несколько циклов parfor

parfor — это удобный способ распределить независимые итерации интенсивных вычислений между несколькими «воркерами». Одно существенное ограничение заключается в том, что parfor-циклы не могут быть вложены, и неизменно это ответ на подобные вопросы, такие как там и там .

Почему так желательно распараллеливание через границы цикла

Рассмотрим следующий фрагмент кода, в котором итерации занимают сильно различающееся количество времени на машине, которая поддерживает 4 рабочих процесса. Оба цикла перебирают 6 значений, которые явно трудно разделить между 4.

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

Кажется хорошей идеей выбрать внутренний цикл для parfor, потому что отдельные вызовы somefun более изменчивы, чем итерации внешнего цикла. Но что, если время выполнения каждого вызова somefun очень похоже? Что, если есть тенденции во времени выполнения, и у нас есть три вложенных цикла? Эти вопросы возникают регулярно, и люди переходят к крайности.

Для объединения циклов требуется шаблон

В идеале somefun выполняется для всех пар row и col, и рабочие должны быть заняты независимо от того, какой iterand изменяется. Решение должно выглядеть так

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, которую я ищу? Есть ли удобный идиоматический шаблон, который кто-то придумал?


person s.bandara    schedule 30.11.2013    source источник


Ответы (3)


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.

person Daniel    schedule 30.11.2013
comment
Использование prod и ind2sub для упаковки и распаковки итерандов делает это действительно изящным. - person s.bandara; 30.11.2013
comment
Я нашел некоторую разницу во времени в указанном вами подходе, пожалуйста, проверьте, 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; - person ; 25.05.2014
comment
@USER: Это абсолютно верно, линеаризованный цикл имеет более высокие накладные расходы, но в сочетании с parfor и некоторыми более сложными вычислениями это решение обычно быстрее. - person Daniel; 26.06.2014
comment
@Daniel Что делать, если мои циклы увеличиваются на значения, отличные от 1? Вот так: пар для ii = 1 : 10 для j = 1 : 4 : 9 для k = 1 : 0,3 : 2 для l = 5 : 40 : 200 - person Trmotta; 12.09.2016
comment
@Trmotta: вы можете преобразовать любой индекс в целые индексы. Вместо for j = 1 : 4 : 9 вы используете j_list=1 : 4 : 9;for j_index=1:numel(j_list),j=j_list(j_index),end. - person Daniel; 12.09.2016

Вы должны быть в состоянии сделать это с bsxfun. Я верю, что bsxfun по возможности будет распараллеливать код (см. здесь для получения дополнительной информации), и в этом случае вы сможете сделать следующее:

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);
person MrAzzaman    schedule 30.11.2013
comment
Спасибо. Мне нравится второй вариант, использующий произведение верхних границ. Это кажется более гибким, чем использование bsxfun, например, в блоке скриптового кода. - person s.bandara; 30.11.2013

Основываясь на ответах @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

Сценарий будет обрабатывать лунки в строках со 2 по 7 и в столбцах с 3 по 10 на планшетах с 1 по 4 без простоя рабочих, пока другие лунки ожидают обработки. В надежде, что это кому-то поможет, я разместил iterlin и iterget на обмене файлами MATLAB. .

person s.bandara    schedule 01.12.2013
comment
Здравствуйте, я видел ваши функции и их пример. Не могли бы вы сказать мне, parfor в вашей функции нужно открыть parpool или matlabpool? как проверить его работоспособность tic toc - person ; 25.05.2014
comment
Не могли бы вы привести пример, в котором вы вводите результаты вычислений в X{k} для каждой комбинации пластины, строки и столбца? Как мне впоследствии получить доступ к результату, например, для строки = 3, столбца = 4 и пластины = 2? - person KAE; 28.11.2018