Как вложить несколько циклов 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 и работники должны быть заняты независимо от того, какая из них меняется. Решение должно выглядеть так

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

3 ответа

Решение

Г-н Аззман уже указал, как линеаризовать вложенные циклы. Вот общее решение для линеаризации 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,

Вы должны быть в состоянии сделать это с 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);

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

Другие вопросы по тегам