Как вложить несколько циклов 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.