Можно ли ускорить вычисление gpuArray с помощью arrayfun() (или как-то иначе)?
У меня есть сложная матрица, и я хотел бы изменить ее
Я стремлюсь сократить время, затрачиваемое на выполнение этих операций. Для 1000 итераций на ЦП я измеряю около 6,4 секунды. Следуя документации Matlab , я смог переместить это на GPU, что сократило затрачиваемое время до 0,07 секунды (невероятное улучшение x91!). Все идет нормально.
Тем не менее, я также сейчас читаю эту ссылку в документах, в которой описывается, как мы можем иногда найти еще большее улучшение для поэлементных вычислений, если мы используем
Nt = 1000; % Number of times to run each method
test_functionFcn = @test_function;
A = rand( 500, 600, 'double' ) + rand( 500, 600, 'double' )*1i; % Define an initial complex matrix
gpu_A = gpuArray(A); % Transfer matrix to a GPU array
%%%%%%%%%%%%%%%%%%%% Run the calculation Nt times on CPU only %%%%%%%%%%%%%%%%%%%%
cpu_data_out = A;
tic
for k = 1:Nt
cpu_data_out = test_function( cpu_data_out );
end
tcpu = toc;
%%%%%%%%%%%%%%%%% Run the calculation Nt times on GPU directly %%%%%%%%%%%%%%%%%%%%
gpu_data_out = gpu_A;
tic
for k = 1:Nt
gpu_data_out = test_function(gpu_data_out);
end
tgpu = toc;
%%%%%%%%%%%%%% Run the calculation Nt times on GPU using arrayfun() %%%%%%%%%%%%%%
gpuarrayfun_data_out = gpu_A;
tic
for k = 1:Nt
gpuarrayfun_data_out = arrayfun( test_functionFcn, gpuarrayfun_data_out );
end
tgpu_arrayfun = toc;
%%% Print results %%%
fprintf( 'Time taken using only CPU: %g\n', tcpu );
fprintf( 'Time taken using gpuArray directly: %g\n', tgpu );
fprintf( 'Time taken using GPU + arrayfun(): %g\n', tgpu_arrayfun );
%%% Function to operate on matrices %%%
function y = test_function(x)
y = exp(-1i*(x + abs(x).^2));
end
и результаты:
Time taken using only CPU: 6.38785
Time taken using gpuArray directly: 0.0680587
Time taken using GPU + arrayfun(): 0.474612
Мои вопросы:
- Правильно ли я использую arrayfun() в этой ситуации, и ожидается, что arrayfun() должно быть хуже?
- Если это так, и действительно ожидается, что он медленнее, чем прямой метод gpuArray, есть ли какой-либо простой (т.е. не-MEX) способ ускорить такой расчет? (Я вижу, что они также упоминают, например, использование pagefun ).
Заранее благодарю за любой совет.
(Видеокарта Nvidia Quadro M4000, я использую Matlab R2017a)
Редактировать
Прочитав ответ @Edric, я думаю, что важно показать немного больше более широкого кода. Одна вещь, которую я не упомянул в OP, заключается в том, что в моем реальном основном коде внутри цикла k=1:Nt есть дополнительная операция, которая представляет собой умножение матриц с транспонированием разреженной трехдиагональной матрицы. Вот более подробный MWE того, что происходит на самом деле:
Nt = 1000; % Number of times to run each method
N_rows = 500;
N_cols = 600;
test_functionFcn = @test_function;
A = rand( N_rows, N_cols, 'double' ) + rand( N_rows, N_cols, 'double' )*1i; % Define an initial complex matrix
%%% Generate a sparse, tridiagonal, square transformation matrix %%%%%%%%
mm = 10*ones(N_cols,1); % Subdiagonal elements
dd = 20*ones(N_cols,1); % Main diagonal elements
pp = 30*ones(N_cols,1); % Superdiagonal elements
M = spdiags([mm dd pp],-1:1,N_cols,N_cols);
M(1,1) = 6; % Set a couple of other entries
M(2,1) = 3;
%%%%%%%%%%%%%%%%%%%% Run the calculation Nt times on CPU only %%%%%%%%%%%%
cpu_data_out = A;
for k = 1:Nt
cpu_data_out = test_function( cpu_data_out );
cpu_data_out = cpu_data_out*M.';
end
%%% Function to operate on matrices %%%
function y = test_function(x)
y = exp(-1i*(x + abs(x).^2));
end
Мне очень жаль, что я не включил это в OP - в то время я не осознавал, что это может иметь отношение к решению. Это меняет дело? Есть ли еще преимущества, которые можно получить с помощью arrayfun() на GPU, или теперь это не подходит для преобразования в arrayfun()?
1 ответ
Несколько пунктов здесь. Во-первых (и самое главное), для тайм-кода на GPU нужно использовать либоgputimeit
, или вам нужно ввести вызовwait(gpuDevice)
прежде чем звонить
toc
. Это связано с тем, что работа запускается асинхронно на графическом процессоре, и вы получаете точные тайминги, только дождавшись ее завершения. С этими небольшими изменениями на моем графическом процессоре я вижу 0,09 секунды для метода и 0,18 секунды для версии.
Выполнение цикла операций графического процессора, как правило, неэффективно, поэтому основной выигрыш, который вы можете здесь получить, заключается в том, что вы поместите цикл внутрь тела функции, чтобы этот цикл выполнялся непосредственно на графическом процессоре. Как это:
%%% Function to operate on matrices %%%
function x = test_function(x,Nt)
for ii = 1:Nt
x = exp(-1i*(x + abs(x).^2));
end
end
Вам нужно будет вызвать его как
A = arrayfun(@test_function, A, Nt)
. На моем графическом процессоре это приносит
arrayfun
время сократилось до 0,05 секунды, так что примерно в два раза быстрее, чем обычный
gpuArray
версия.