Я пытаюсь реализовать алгоритм с использованием больших плотных матриц в Matlab.Я использую экземпляры AWS с несколькими графическими процессорами для повышения производительности.
На каждой итерации мне приходится работать с двумя большими матрицами m
на n
(двойных), A и B, где m = 1600000
,и n = 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 одновременно.
Из моих экспериментов выше, я заметил, что фактические вычислительные затратыГПУ, работающая над задачами линейной алгебры, оказывается низкой.Как представляется, ключевым узким местом является время, затрачиваемое на параллельную загрузку данных из ЦП в несколько графических процессоров и сбор данных из нескольких графических процессоров обратно в ЦП, хотя также возможно, что в игре есть какой-то другой фактор.
Вместо этого у меня есть следующие вопросы:
Что именно лежит в основе медлительности parfor
?Почему затраты на связь (или по какой-либо другой причине) так дороги?
Как я могу ускорить параллельную загрузку и выгрузку данных из ЦП в несколько графических процессоров, а затем обратно в Matlab?Существуют ли уловки, включающие parfor
, spmd
(или другие вещи, такие как parfeval
, которые я не пробовал), которыми я пренебрег?Или я достиг некоторого фундаментального ограничения скорости в Matlab (при условии, что я сохраняю текущие настройки CPU / GPU)?
Если есть фундаментальное ограничение в том, как Matlab обрабатывает загрузку данных /выгрузка, единственным выходом будет переписать эту часть кода на C ++?
Спасибо за любую помощь!