Вложенный parfor и for - циклы и другие требования parfor

Вложенный parfor - циклы

Вы не можете использовать parfor - цикл в другом parfor - цикл. Как пример, не позволено следующее вложение parfor - циклы:

   parfor i = 1:10
       parfor j = 1:5
           ...
       end
   end

Совет

Вы не можете вложить parfor непосредственно в другом parfor - цикл. parfor - цикл может вызвать функцию, которая содержит parfor - цикл, но вы не получаете дополнительного параллелизма.

Анализатор кода в Редакторе MATLAB® отмечает использование parfor в другом parfor - цикл:

Вы не можете вложить parfor - циклы, потому что распараллеливание может быть выполнено только на одном уровне. Поэтому выберите который цикл запуститься параллельно, и преобразовать другой цикл в for - цикл.

Рассмотрите следующие проблемы производительности при контакте с вложенными циклами:

  • Параллельная обработка подвергается наверху. Обычно необходимо запустить внешний цикл параллельно, потому что наверху только происходит однажды. Если при запуске внутренний цикл параллельно, то каждое несколько выполнения parfor подвергается издержкам. Смотрите Преобразовывают Вложенные циклы for в циклы parfor для примера, как измерить параллель наверху.

  • Убедитесь, что количество итераций превышает количество рабочих. В противном случае вы не используете всех доступных рабочих.

  • Попытайтесь сбалансировать parfor - времена итерации цикла. parfor пытается компенсировать некоторую неустойчивость загрузки.

Совет

Всегда запускайте наиболее удаленный цикл параллельно, потому что вы уменьшаете параллель наверху.

Можно также использовать функцию, которая использует parfor, и встройте его в parfor - цикл. Распараллеливание происходит только на внешнем уровне. В следующем примере вызовите функциональный MyFun.m во внешнем parfor - цикл. Внутренний parfor - цикл, встроенный в MyFun.m, запускается последовательно, не параллельно.

parfor i = 1:10
    MyFun(i)
end

function MyFun(i)
parfor j = 1:5
    ...
end
end

Совет

Вложенный parfor - циклы обычно не приносят вам вычислительной пользы.

Преобразуйте вложенный for - циклы к parfor - циклы

Типичное использование вложенных циклов должно продвинуться через массив с помощью одной переменной цикла, чтобы индексировать одну размерность и переменную вложенного цикла, чтобы индексировать другую размерность. Каноническая форма:

X = zeros(n,m);
for a = 1:n
    for b = 1:m
        X(a,b) = fun(a,b)
    end
end

Следующий код показывает простой пример. Используйте tic и toc, чтобы измерить вычислительное необходимое время.

A = 100;
tic
for i = 1:100
    for j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
toc
Elapsed time is 49.376732 seconds.

Можно параллелизировать любой из вложенных циклов, но вы не можете запустить обоих параллельно. Причина состоит в том, что рабочие в параллельном пуле не могут запустить, или доступ далее параллельны пулам.

Если цикл, считаемый i, преобразован в parfor - цикл, то каждый рабочий в пуле выполняет вложенные циклы с помощью счетчика цикла j. Сами циклы j не могут запуститься как parfor на каждом рабочем.

Поскольку параллельная обработка подвергается наверху, необходимо выбрать тщательно, хотите ли вы преобразовать или внутреннее или внешний for - цикл к parfor - цикл. Следующий пример показывает, как измерить параллель наверху.

Сначала преобразуйте только внешний for - цикл к parfor - цикл. Используйте tic и toc, чтобы измерить вычислительное необходимое время. Используйте ticBytes и tocBytes, чтобы измериться, сколько данных передается и от рабочих в параллельном пуле.

Запустите новый код и запустите его снова. Первый показ медленнее, чем последующие выполнения, потому что параллельный пул занимает время, чтобы запустить и сделать код доступным для рабочих.

A = 100;
tic
ticBytes(gcp);
parfor i = 1:100
    for j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
tocBytes(gcp)
toc
             BytesSentToWorkers    BytesReceivedFromWorkers
             __________________    ________________________

    1             32984                 24512              
    2             33784                 25312              
    3             33784                 25312              
    4             34584                 26112              
    Total    1.3514e+05            1.0125e+05              

Elapsed time is 14.130674 seconds.

Затем преобразуйте только внутренний цикл в parfor - цикл. Измерьте необходимое время и данные, переданные как в предыдущем случае.

A = 100;
tic
ticBytes(gcp);
for i = 1:100
    parfor j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
tocBytes(gcp)
toc
             BytesSentToWorkers    BytesReceivedFromWorkers
             __________________    ________________________

    1        1.3496e+06             5.487e+05              
    2        1.3496e+06            5.4858e+05              
    3        1.3677e+06            5.6034e+05              
    4        1.3476e+06            5.4717e+05              
    Total    5.4144e+06            2.2048e+06              

Elapsed time is 48.631737 seconds.

Если вы преобразовываете внутренний цикл в parfor - цикл, и время и переданный объем данных намного больше, чем в параллельном внешнем цикле. В этом случае прошедшее время является почти тем же самым как во вложенном for - пример цикла. Ускорение меньше, чем выполнение внешнего цикла параллельно, потому что у вас есть больше передачи данных и таким образом больше параллели наверху. Поэтому, если вы выполняете внутренний цикл параллельно, вы не извлекаете вычислительной пользы по сравнению с выполнением последовательного for - цикл.

Если вы хотите уменьшать параллель наверху и ускорить ваше вычисление, запустить внешний цикл параллельно.

Если вы преобразовываете внутренний цикл вместо этого, то каждая итерация внешнего цикла инициирует отдельный parfor - цикл. Таким образом, преобразование внутреннего цикла создает 100 parfor - циклы. Каждое несколько выполнения parfor подвергается наверху. Если вы хотите уменьшать параллель наверху, необходимо запустить внешний цикл параллельно вместо этого, потому что наверху только происходит однажды.

Совет

Если вы хотите ускорить свой код, всегда запускать внешний цикл параллельно, потому что вы уменьшаете параллель наверху.

Вложенный for - циклы: требования и ограничения

Если вы хотите преобразовать вложенный for - цикл к parfor - цикл, необходимо гарантировать, что переменные цикла правильно классифицируются, смотрите Переменные Поиска и устранения неисправностей в циклах parfor. Если ваш код не придерживается инструкций и ограничений, маркированных как требуется, вы получаете ошибку. MATLAB фиксирует некоторые из этих ошибок в то время, когда он читает код и других, когда он выполняет код. Эти ошибки маркированы как требуется (статичными) или Необходимыми (динамический) соответственно.

Необходимые (помехи): необходимо задать область значений for - цикла, вложенного в parfor - цикл постоянными числами или широковещательно передавать переменные.

В следующем примере слева не работает код, потому что вы задаете верхний предел for - цикл вызовом функции. Код справа предоставляет обходное решение первым определением широковещательной передачи или постоянной переменной вне parfor - цикл:

НедопустимыйДопустимый
A = zeros(100, 200);
parfor i = 1:size(A, 1)
   for j = 1:size(A, 2)
      A(i, j) = i + j;
   end
end
A = zeros(100, 200);
n = size(A, 2);
parfor i = 1:size(A,1)
   for j = 1:n
      A(i, j) = i + j;
   end
end
Необходимые (помехи): индексная переменная для вложенного for - цикл никогда не должна явным образом присваиваться кроме в его for - цикл.

Это ограничение требуется, потому что, изменяя вложенный for - переменная цикла в теле цикла не может гарантировать, что область, индексированная for - переменная цикла, доступна в каждом рабочем.

Код слева не допустим, потому что он пытается изменить значение вложенного for - переменная цикла j в теле цикла. Код справа предоставляет обходное решение путем присвоения вложенного for - переменная цикла к временной переменной t, и затем обновления t.

НедопустимыйДопустимый
A = zeros(10); 
parfor i = 1:10 
   for j = 1:10 
       A(i, j) = 1; 
       j = j+1; 
   end
end
A = zeros(10); 
parfor i = 1:10 
   for j = 1:10 
    A(i, j) = 1;  
    t = j;
    t = t + 1;
   end
end
Необходимые (помехи): Вы не можете индексировать или преобразовать вложенную переменную цикла for в нижний индекс.

Это ограничение требуется, потому что индексация переменной цикла не может гарантировать независимость итераций.

Пример слева недопустим, потому что он пытается индексировать вложенный for - переменная цикла j. Пример справа удаляет эту индексацию.

НедопустимыйДопустимый
A = zeros(10); 
parfor i = 1:10 
   for j = 1:10 
       j(1);
   end
end
A = zeros(10); 
parfor i = 1:10 
   for j = 1:10 
       j;
   end
end
Необходимые (помехи): При использовании вложенного for - переменная цикла для индексации нарезанного массива, необходимо использовать переменную в простой форме, не как часть выражения.

Например, следующий код слева не работает, но код справа делает:

НедопустимыйДопустимый
A = zeros(4, 11);
parfor i = 1:4
   for j = 1:10
      A(i, j + 1) = i + j;
   end
end
A = zeros(4, 11);
parfor i = 1:4
   for j = 2:11
      A(i, j) = i + j - 1;
   end
end
Необходимые (помехи): Если вы используете вложенный for - цикл, чтобы индексировать в нарезанный массив, вы не можете использовать тот массив в другом месте в parfor - цикл.

В следующем примере слева не работает код, потому что A нарезан и индексирован во вложенном for - цикл. Код справа работает, потому что v присвоен A за пределами вложенного цикла:

НедопустимыйДопустимый
A = zeros(4, 10);
parfor i = 1:4
    for j = 1:10
        A(i, j) = i + j;
    end
    disp(A(i, j))
end
A = zeros(4, 10);
parfor i = 1:4
    v = zeros(1, 10);
    for j = 1:10
        v(j) = i + j;
    end
    disp(v(j))
    A(i, :) = v;
end
Необходимые (помехи): нарезанная выходная переменная может использоваться только в одном вложенном цикле for.

Предположим, что вы используете несколько for - циклы (не вложенный друг в друге) в parfor - цикл, чтобы индексировать в один нарезанный массив. В этом случае for - циклы должны циклично выполниться в той же области значений значений. В следующем примере код слева не работает потому что j и цикл k по различным значениям. Код справа работает, чтобы индексировать различные фрагменты нарезанного массива A:

НедопустимыйДопустимый
A = zeros(4, 10);
parfor i = 1:4
   for j = 1:5
      A(i, j) = i + j;
   end
   for k = 6:10
      A(i, k) = pi;
   end
end
A = zeros(4, 10);
parfor i = 1:4
   for j = 1:10
      if j < 6
         A(i, j) = i + j;
      else
         A(i, j) = pi;
      end
   end
end

parfor- ограничения цикла

Вложенные функции

Тело parfor - цикл не может сослаться на вложенную функцию. Однако это может вызвать вложенную функцию указателем на функцию. Попробуйте следующий пример. Обратите внимание на то, что A(idx) = nfcn(idx) в parfor - цикл не работает. Необходимо использовать feval, чтобы вызвать указатель fcn в parfor - тело цикла.

function A = pfeg
    function out = nfcn(in)
        out = 1 + in;
    end

fcn = @nfcn;

parfor idx = 1:10
    A(idx) = feval(fcn, idx); 
end

end
>> pfeg
Starting parallel pool (parpool) using the 'local' profile ... connected to 4 workers.

ans =

     2     3     4     5     6     7     8     9    10    11

Совет

Если вы используете указатели на функцию, которые относятся к вложенным функциям в parfor - цикл, то значения внешне ограниченных по объему переменных не синхронизируются среди рабочих.

Вложенный parfor - циклы

Тело parfor - цикл не может содержать parfor - цикл. Для получения дополнительной информации смотрите Вложенные циклы parfor.

Вложенные операторы spmd

Тело parfor - цикл не может содержать оператор spmd, и оператор spmd не может содержать parfor - цикл. Причина состоит в том, что рабочие не могут запустить, или доступ далее параллельны пулам.

break и операторы return

Тело parfor - цикл не может содержать операторы break или return. Рассмотрите parfeval или parfevalOnAll вместо этого, потому что можно использовать cancel на них.

Глобальные и персистентные переменные

Тело parfor - цикл не может содержать объявления переменной persistent или global. Причина состоит в том, что эти переменные не синхронизируются между рабочими. Можно использовать переменные global или persistent в функциях, но их значение видимо только рабочему, который создает их. Вместо переменных global это - лучшая практика, чтобы использовать аргументы функции для стоимости акций.

Чтобы узнать больше о переменных требованиях, смотрите Переменные Поиска и устранения неисправностей в циклах parfor.

Скрипты

Если скрипт вводит переменную, вы не можете вызвать этот скрипт из parfor - оператор spmd или цикл. Причина состоит в том, что этот скрипт вызвал бы нарушение прозрачности. Для получения дополнительной информации смотрите, Гарантируют Прозрачность в циклах parfor или spmd Операторах.

Анонимные функции

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

x = 1:10;
parfor i=1:10
    temp = x(i);
    anonymousFunction = @() 2*temp;
    x(i) = anonymousFunction() + i;
end
disp(x);

Для получения дополнительной информации о нарезанных переменных смотрите Нарезанные Переменные.

Функции inputname

Используя inputname, чтобы возвратить имя переменной рабочей области, соответствующее номеру аргумента, не поддержан в parfor - циклы. Причина состоит в том, что у рабочих parfor нет доступа к рабочей области рабочего стола MATLAB. Чтобы работать вокруг этого, вызовите inputname перед parfor, как показано в следующем примере.

a = 'a';
myFunction(a)

function X = myFunction(a)
name = inputname(1);
X = [];
parfor i=1:2
    X = strcat(X,name);
end
end
    

Функции load

Синтаксисы load, которые не присваивают выходной структуре, не поддержаны в parfor - циклы. В parfor всегда присваивайте вывод load к структуре.

nargin или функции nargout

Следующее использование не поддержано в parfor - циклы:

  • Используя nargin или nargout без аргумента функции

  • Используя narginchk или nargoutchk, чтобы проверить количество аргументов ввода или вывода в вызове функции, которая является выполняющимся в данным моментом

Причина состоит в том, что у рабочих нет доступа к рабочей области рабочего стола MATLAB. Чтобы работать вокруг этого, вызовите эти функции перед parfor, как показано в следующем примере.

myFunction('a','b')

function X = myFunction(a,b)
nin = nargin;
parfor i=1:2
    X(i) = i*nin;
end
end
    

Скрипты псевдокода

Можно вызвать файлы скрипта Псевдокода из parfor - цикл, но скрипты Псевдокода не могут содержать parfor - цикл. Чтобы работать вокруг этого, используйте функцию Псевдокода вместо скрипта Псевдокода.

Смотрите также

| |

Похожие темы