Можно ли ускорить вычисление gpuArray с помощью arrayfun() (или как-то иначе)?

У меня есть сложная матрица, и я хотел бы изменить ее раз в соответствии с . Размер обычно составляет 1000x1000, а количество запусков будет около 10000.

Я стремлюсь сократить время, затрачиваемое на выполнение этих операций. Для 1000 итераций на ЦП я измеряю около 6,4 секунды. Следуя документации Matlab , я смог переместить это на GPU, что сократило затрачиваемое время до 0,07 секунды (невероятное улучшение x91!). Все идет нормально.

Тем не менее, я также сейчас читаю эту ссылку в документах, в которой описывается, как мы можем иногда найти еще большее улучшение для поэлементных вычислений, если мы используем также. Если я попытаюсь следовать руководству, время на самом деле будет хуже, 0,47 секунды. Мои тесты показаны ниже:

      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

Мои вопросы:

  1. Правильно ли я использую arrayfun() в этой ситуации, и ожидается, что arrayfun() должно быть хуже?
  2. Если это так, и действительно ожидается, что он медленнее, чем прямой метод 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версия.

Другие вопросы по тегам