Улучшиться parfor Производительность

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

Где создать массивы

Когда вы создаете большой массив в клиенте перед вашим parfor- цикл и доступ это в цикле, вы можете наблюдать медленное подписание своего кода. Чтобы улучшать производительность, скажите каждому рабочему MATLAB® создавать его собственные массивы или фрагменты их, параллельно. Можно сэкономить время передачи данных от клиента рабочим, прося, чтобы каждый рабочий создал его собственную копию этих массивов, параллельно, в цикле. Рассмотрите изменение вашей обычной практики инициализации переменных перед for- цикл, избегая бесполезного повторения в цикле. Вы можете найти, что параллельное создание массивов в цикле улучшает производительность.

Повышение производительности зависит от различных факторов, включая

  • размер массивов

  • время должно было создать массивы

  • доступ рабочего ко всем или части массивов

  • количество итераций цикла, которые выполняет каждый рабочий

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

Как альтернатива, рассмотрите parallel.pool.Constant функция, чтобы установить переменные на рабочих пула перед циклом. Эти переменные остаются на рабочих после концов цикла и остаются доступными для нескольких parfor- циклы. Вы можете улучшить использование эффективности parallel.pool.Constant, потому что данные передаются только однажды рабочим.

В этом примере вы сначала создаете большой набор данных D и выполните a parfor- доступ цикла D. Затем вы используете D создавать a parallel.pool.Constant объект, который позволяет вам снова использовать данные путем копирования D каждому рабочему. Измерьте прошедшее время с помощью tic и toc для каждого случая и примечания различие.

function constantDemo
    D = rand(1e7, 1);
    tic
    for i = 1:20
        a = 0;
        parfor j = 1:60
            a = a + sum(D);
        end
    end
    toc
    
    tic
    D = parallel.pool.Constant(D);
    for i = 1:20
        b = 0;
        parfor j = 1:60
            b = b + sum(D.Value);
        end
    end
    toc
end
>> constantDemo
Starting parallel pool (parpool) using the 'local' profile ... connected to 4 workers.
Elapsed time is 63.839702 seconds.
Elapsed time is 10.194815 seconds.
Во втором случае вы отправляете данные только однажды. Можно улучшить эффективность parfor- цикл при помощи parallel.pool.Constant объект.

Профилирование parfor- циклы

Можно профилировать a parfor- цикл путем измерения времени протек с помощью tic и toc. Можно также измериться, при помощи какого количества данные передаются и от рабочих в параллельном пуле ticBytes и tocBytes. Обратите внимание на то, что это отличается от профильного кода MATLAB в обычном смысле с помощью профилировщика MATLAB, смотрите Профиль Код, чтобы Улучшать Производительность.

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

  1. В редакторе MATLAB введите следующий for- цикл. Добавьте tic и toc измерять время протекло. Сохраните файл как MyForLoop.m.

    function a = MyForLoop(A)
        tic
        for i = 1:200
            a(i) = max(abs(eig(rand(A))));
        end
        toc
    end
  2. Запустите код и отметьте прошедшее время.

    a = MyForLoop(500);
    Elapsed time is 31.935373 seconds.

  3. \in MyForLoop.m, замените for- цикл с a parfor- цикл. Добавление ticBytes и tocBytes измеряться, сколько данных передается и от рабочих в параллельном пуле. Сохраните файл как MyParforLoop.m.

    ticBytes(gcp);
    parfor i = 1:200
        a(i) = max(abs(eig(rand(A))));
    end
    tocBytes(gcp)

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

    По умолчанию MATLAB автоматически открывает параллельный пул рабочих на вашей локальной машине.

    a = MyParforLoop(500);
    Starting parallel pool (parpool) using the 'local' profile ... connected to 4 workers.
    ...
                 BytesSentToWorkers    BytesReceivedFromWorkers
                 __________________    ________________________
    
        1        15340                  7024                   
        2        13328                  5712                   
        3        13328                  5704                   
        4        13328                  5728                   
        Total    55324                 24168                   
    
    Elapsed time is 10.760068 seconds. 
    Прошедшее время составляет 31,9 секунды в сериале и 10,8 секунд параллельно и показывает, что этот код извлекает выгоду от преобразования до a parfor- цикл.

Разрезание массивов

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

Как пример, вы первый показ parfor- цикл с помощью нарезанной переменной и меры прошедшее время.

% Sliced version

M = 100;
N = 1e6;
data = rand(M, N);

tic
parfor idx = 1:M
    out2(idx) = sum(data(idx, :)) ./ N;
end
toc
Elapsed time is 2.261504 seconds.

Теперь предположите, что вы случайно используете ссылку на переменную data вместо N в parfor- цикл. Проблема здесь состоит в том что вызов size(data, 2) преобразует нарезанную переменную в (ненарезанную) переменную широковещательной передачи.

% Accidentally non-sliced version

clear

M = 100;
N = 1e6;
data = rand(M, N);

tic
parfor idx = 1:M
    out2(idx) = sum(data(idx, :)) ./ size(data, 2);
end
toc
Elapsed time is 8.369071 seconds.
Обратите внимание на то, что прошедшее время больше для случайно широковещательно переданной переменной.

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

Оптимизация на локальном по сравнению с кластерными рабочими

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

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

Вы можете экспериментировать, чтобы видеть, быстрее ли это, чтобы создать массивы, прежде чем цикл (как показано слева ниже), вместо того, чтобы иметь каждого рабочего создадут его собственные массивы в цикле (как показано справа).

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

parpool('local')

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

tic;
n = 200;
M = magic(n);
R = rand(n);
parfor i = 1:n
    A(i) = sum(M(i,:).*R(n+1-i,:));
end
toc
tic;
n = 200;
parfor i = 1:n
    M = magic(n);
    R = rand(n);
    A(i) = sum(M(i,:).*R(n+1-i,:));
end
toc

Работая на удаленном кластере, вы можете найти различное поведение, когда рабочие могут одновременно создать свои массивы, сохранив время трансфера. Поэтому код, который оптимизирован для локальных рабочих, не может быть оптимизирован для кластерных рабочих, и наоборот.

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

Похожие темы