Эффективный способ подсчета суммы (a. * Exp (b. *c), 1) с использованием графического процессора MATLAB - PullRequest
4 голосов
/ 06 февраля 2020

У меня есть код MATLAB с ускорением на GPU, который тратит 80% -90% своего времени на вычисления

sum(a.*exp(b.*c),1)

, где

size(a) = [n 1]
size(b) = [n 1]
size(c) = [1 m]

n можно выбрать для быть сколь угодно большим (в пределах ограничений памяти)

5000 <<strong> m <20000 </p>

Я хотел бы ускорить это больше, чем просто используя gpuArrays (примерно 17x для удвоения точность).

Сравнительный анализ

Используя MATLAB 2018b и графический процессор NVIDIA P100, я запустил следующий скрипт, чтобы найти оптимальный размер n . Это показывает, что я достигаю 17-кратного ускорения по сравнению с процессором (Dual Socket Intel Xeon E5-2650v2) с двойной точностью. Могу ли я улучшить это, сделав что-то более продвинутое, например, используя GPU-кодер, или даже совместно используемую память или текстурную память, как описано ниже? https://uk.mathworks.com/help/parallel-computing/examples/accessing-advanced-cuda-features-using-mex.html

%% Optimisation MWE

nVec = 1000:1000:60000; % Vector of candidate n values
m = 5000;

f1 = figure(1);
ax(1) = subplot(3,1,1);
ax(2) = subplot(3,1,2);
ax(3) = subplot(3,1,3);

% Preallocate time outputs
t = nan(length(nVec),3);
speedupGPU = nan(length(nVec),2);

% Loop over candidate n values
for n = 1:length(nVec)

    %% CPU code
    a = rand(nVec(n),1);
    b = rand(nVec(n),1);
    c = rand(1,m);

    f1 = @() sum(a.*exp(b.*c),1);

    t(n,1) = timeit(f1,1);

    %% GPU code (double precision)
    a = gpuArray(a);
    b = gpuArray(b);
    c = gpuArray(c);

    f2 = @() sum(a.*exp(b.*c),1);

    t(n,2) = gputimeit(f2);

    %% GPU code (single precision)
    a = single(a);
    b = single(b);
    c = single(c);

    f3 = @() sum(a.*exp(b.*c),1);

    t(n,3) = gputimeit(f3);

    %% Calculate speedup
    speedupGPU(n,1) = t(n,1)/t(n,2);
    speedupGPU(n,2) = t(n,1)/t(n,3);

    %% Plot
    plot(ax(1),nVec,t,'.-')             % Plot compute time
    plot(ax(2),nVec,t./nVec(:),'.-')    % Plot normalised compute time
    plot(ax(3),nVec,speedupGPU,'.-')    % Plot Speedup

    %% Label plots
    xlabel(ax(1),'n')
    ylabel(ax(1),'Time')
    legend(ax(1),'CPU','GPU double','GPU single')

    xlabel(ax(2),'n')
    ylabel(ax(2),'Normalised Time')
    legend(ax(2),'CPU','GPU double','GPU single')

    xlabel(ax(3),'n')
    ylabel(ax(3),'Speedup')
    legend(ax(3),'CPU/GPU double','CPU/GPU single')

    drawnow

end

Это приводит к следующему рисунку (вверху: время выполнения с увеличением n (чем меньше, тем лучше), в середине: время выполнения, нормализованное на n (чем меньше, тем лучше) , снизу: ускорение относительно процессора (чем больше, тем лучше)):

Execution time, normalised execution time, and speedup over various **n**

1 Ответ

4 голосов
/ 06 февраля 2020

Я понимаю, что это может не дать вам нужного ускорения, но один из способов повысить производительность этого кода - избавиться от sum с помощью умножения матриц:

sum(a.*exp(b.*c),1) --> a.'*exp(b.*c)

В моей системе это привело к увеличению ускорения с ~ 10 до ~ 15.

Я должен также упомянуть, что для самого низкого значения n я получил ускорение ~ 20, также заменив умножение массива ( .*) с умножением матрицы (*): a.'*exp(b.*c) --> a.'*exp(b*c).

Протестировано на R2019b, Win10, GTX660.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...