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

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

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

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

Совет

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

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

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

Примите во внимание следующие проблемы эффективности при работе с вложенными циклами:

  • Параллельная обработка сопряжена с накладными расходами. Обычно следует запускать внешний контур параллельно, потому что накладные расходы возникают только один раз. Если вы запускаете внутренний цикл параллельно, то каждый из нескольких parfor выполнение влечет за собой накладные расходы. Пример измерения параллельных накладных расходов см. в разделе Преобразование вложенных циклов для циклов в циклы 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-loop, затем каждый рабочий процесс в пуле выполняет вложенные циклы с помощью j счетчик цикла. The 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-loop, и время, и количество переданных данных намного больше, чем в параллельном внешнем контуре. В этом случае прошедшее время почти совпадает с временем во вложенном forЦикл. Скорость меньше, чем параллельный запуск внешнего контура, потому что у вас больше передачи данных и, следовательно, больше параллельных накладных расходов. Поэтому, если вы выполняете внутренний цикл параллельно, вы не получаете вычислительных преимуществ по сравнению с запуском последовательного for-цикл.

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

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

Совет

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

Вложенные for-Циклы: Требования и ограничения

Если вы хотите преобразовать вложенное for-цикл на parfor-loop, необходимо убедиться, что переменные цикла правильно классифицированы, см. «Поиск и устранение проблем с переменными в циклах 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Цикл изменяется в любом месте parfor-цикл кроме его for оператор, область, индексированная for- переменная loop не гарантирована для каждого работника.

Код слева недопустим, поскольку он пытается изменить значение вложенного 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Цикл индексирована, итерации не гарантированы как независимые.

Пример слева недопустим, поскольку он пытается индексировать вложенную 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

parfor-Цикл ограничений

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

Тело a 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 Операторы

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

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

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

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

Тело parfor-цикл не может содержать global или persistent объявления переменных. Причина в том, что эти переменные не синхронизируются между работниками. Вы можете использовать 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);
    
    parfor i=1:2
        X(i).(name) = i;
    end
end

load Функции

Синтаксисы load которые не назначаются структурой output не поддерживаются внутри 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

Скрипты P-кода

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

См. также

| |

Похожие темы