Как ускорить параллельную загрузку (и выгрузку) матриц на несколько графических процессоров в Matlab - PullRequest
0 голосов
/ 27 апреля 2019

Я пытаюсь реализовать алгоритм с использованием больших плотных матриц в Matlab.Я использую экземпляры AWS с несколькими графическими процессорами для повышения производительности.

На каждой итерации мне приходится работать с двумя большими матрицами m на n (двойных), A и B, где m = 1600000n = 500.Из-за размера матриц и объема памяти каждого графического процессора (~ 8 ГБ памяти каждая) я разбираю проблему, разбивая матрицы по строкам на K порций меньших матриц с одинаковым числом nстолбцы, но меньше строк (M /K).

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

Чтобы добиться этого, я попытался использовать цикл parforв Matlab (с инструментарием параллельных вычислений), используя лучшие практики, такие как нарезка, загрузка только соответствующих данных для каждого работника.Для потомков вот полный фрагмент кода.В этом посте я подробно описал небольшие разложенные проблемы.

M = 1600000;
K = 4;
m = M/K;
n = 500;
A = randn(K, m,n);
B = randn(K,m,n);
C = randn(n,2);
D = zeros(K,m,2);

%delete(gcp('nocreate'));
%p = parpool('local',K);

tic
toc_load = zeros(K,1);
toc_compute = zeros(K,1);
toc_unload = zeros(K,1);
parfor j = 1:K
    tic
    A_blk = gpuArray(reshape(A(j,:,:),[m,n]));
    B_blk = gpuArray(reshape(B(j,:,:), [m,n]));
    C_blk = gpuArray(C);
    D_blk = gpuArray(reshape(D(j,:,:), [m,2]));
    toc_load(j)  = toc;
    tic
    B_blk = D_blk * C_blk' + A_blk + B_blk;
    toc_compute(j) = toc;
    tic
    B(j,:,:) = gather(B_blk);
    toc_unload(j) = toc;
end
toc_all = toc;
fprintf('averaged over 4 workers, loading onto GPU took %f seconds \n', mean(toc_load));
fprintf('averaged over 4 workers, computation on GPU took %f seconds \n',mean(toc_compute));
fprintf('averaged over 4 workers, unloading from GPU took %f seconds \n', mean(toc_unload));
fprintf('the entire process took %f seconds \n', toc_all);

Использование средства проверки времени tic-toc (я запускаю код только после запуска пула, чтобы убедиться в точности отслеживания времени), яобнаружил, что каждому работнику в среднем требуется:

  • 6,33 секунды для загрузки данных в графический процессор
  • 0,18 секунды для запуска вычислений в графическом процессоре
  • 4,91 секунды длявыгрузите данные из графического процессора.

Однако весь процесс занимает 158,57 секунды.Таким образом, коммуникационные издержки (или что-то еще?) Заняли значительную часть времени выполнения.

Затем я попробовал простой цикл for без распараллеливания, см. Фрагмент ниже.

%% for loop
tic
for j = 1:K
    A_blk = gpuArray(reshape(A(j,:,:),[m,n]));
    B_blk = gpuArray(reshape(B(j,:,:), [m,n]));
    C_blk = gpuArray(C);
    D_blk = gpuArray(reshape(D(j,:,:), [m,2]));
    toc_load(j)  = toc;
    B_blk = D_blk * C_blk' + A_blk + B_blk;
    toc_compute(j) = toc;
    B(j,:,:) = gather(B_blk);
end
toc_all = toc;
fprintf('the entire process took %f seconds \n', toc_all);

На этот раз выполнение всего кода заняло всего 27,96 секунды.Таким образом, выполнение кода в последовательном режиме значительно улучшило производительность в этом случае.Тем не менее, учитывая, что у меня есть 4 графических процессора, кажется разочаровывающим, что я не могу получить ускорение, используя все 4 одновременно.

Из моих экспериментов выше, я заметил, что фактические вычислительные затратыГПУ, работающая над задачами линейной алгебры, оказывается низкой.Как представляется, ключевым узким местом является время, затрачиваемое на параллельную загрузку данных из ЦП в несколько графических процессоров и сбор данных из нескольких графических процессоров обратно в ЦП, хотя также возможно, что в игре есть какой-то другой фактор.

Вместо этого у меня есть следующие вопросы:

  1. Что именно лежит в основе медлительности parfor?Почему затраты на связь (или по какой-либо другой причине) так дороги?

  2. Как я могу ускорить параллельную загрузку и выгрузку данных из ЦП в несколько графических процессоров, а затем обратно в Matlab?Существуют ли уловки, включающие parfor, spmd (или другие вещи, такие как parfeval, которые я не пробовал), которыми я пренебрег?Или я достиг некоторого фундаментального ограничения скорости в Matlab (при условии, что я сохраняю текущие настройки CPU / GPU)?

  3. Если есть фундаментальное ограничение в том, как Matlab обрабатывает загрузку данных /выгрузка, единственным выходом будет переписать эту часть кода на C ++?

Спасибо за любую помощь!

1 Ответ

0 голосов
/ 29 апреля 2019

Отправка данных в / из экземпляров AWS для использования с parfor значительно медленнее, чем при использовании рабочих на вашем локальном компьютере, потому что (a) машины находятся дальше, и (b) есть дополнительные издержки, потому что все коммуникации с работниками AWS используютбезопасная связь.

Вы можете использовать ticBytes и tocBytes, чтобы увидеть объем передаваемых данных.

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

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

Иногда существует золотая середина, где у вас есть небольшие фундаментальные данные, которые могут быть вычислены только на клиенте, и большие производные данные, которые необходимы рабочим.В этом случае вы можете соединить вычисления с parallel.pool.Constant или просто сделать все внутри одного блока spmd или чего-то еще.(Ваш цикл parfor в том виде, в котором он написан, может в равной степени использовать spmd, поскольку вы упорядочиваете вещи так, чтобы по одной итерации на одного работника).

...