Почему MATLAB gpuarray намного медленнее просто добавляет две матрицы?

Недавно я использовал библиотеку MATLAB CUDA для некоторых абсолютно простых матричных вычислений на GPU. Но результаты производительности очень странные. может ли какой-либо орган помочь мне понять, что именно происходит и как я могу решить проблему. Заранее спасибо. Обратите внимание, что следующие коды выполняются на GeForce GTX TITAN black gpu.

предположим, что a0,a1,...a6 равны 1000*1000 gpuarrays и U=0,5, а V=0,0

titan = gpuDevice();
tic();

for i=1:10000
a6(1,1)=(0.5.*(a5(1,1)-a0(1,1)))-(a1(1,1)+a2(1,1)+a3(1,1))-(a5(1,1).*U./3.0)-(a5(1,1).*V./2.0)+(0.25.*a5(1,1).*a4(1,1));  
end

wait(titan);
time = toc()

результат за время =17,98 секунды

Теперь переопределим a0,a1,...a6 и U и V для работы на CPU и вычислим необходимое время:

tic();

for i=1:10000
a6(1,1)=(0.5.*(a5(1,1)-a0(1,1)))-(a1(1,1)+a2(1,1)+a3(1,1))-(a5(1,1).*U./3.0)-(a5(1,1).*V./2.0)+(0.25.*a5(1,1).*a4(1,1));  
end

time= toc()  

результат за время = 0,0098 секунды

поэтому более чем в 1800 раз быстрее на процессоре!!!!

Затем я решил сделать предыдущие расчеты по всей матрице, а не по конкретным элементам, и вот результаты:

Результаты для запуска на GPU:

titan = gpuDevice();
tic();
for i=1:10000
a6=(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end
wait(titan);
time = toc()   

результат для времени =6,32 секунды, что означает, что операция на всей матрице выполняется намного быстрее, чем на конкретном элементе!

Результаты для запуска на CPU:

tic();
for i=1:10000
a6=(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end

time= toc()  

результат за время =35,2 секунды

И вот самый удивительный результат: предположим, что a0,a1,...a6 и U и V равны 1*1 gpuarrays, и запустим следующее:

titan = gpuDevice();
tic();
for i=1:10000
a6=(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end
wait(titan);
time = toc()  

результат за время =7,8 секунды

это даже медленнее, чем соответствующий случай 1000 * 1000!

К сожалению, линия a6(1,1)=(0,5.*(A5(1,1)-a0(1,1)))-(a1(1,1)+a2(1,1)+a3(1,1))-. (a5(1,1) *U./3.0)-(a5(1,1).*V./2.0)+(0.25.*a5(1,1).*a4(1,1)); это одна из линий из примерно 100 строк, все в одном цикле for, и эта линия зарекомендовала себя как реальное узкое место, занимая около 50% всего необходимого времени расчета! кто-нибудь может мне помочь? обратите внимание, что передача этой части вычислений на процессор не является выбором, потому что линия узкого места находится в цикле for, и отправка a1,... a6 в процессор и вызов результатов в gpu в каждой итерации занимает гораздо больше времени. любой совет действительно очень ценится.

2 ответа

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

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

tmp = (0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);
a6(1,1) = tmp(1,1);

Ehsan,

Титан могущественен.

Я надеюсь, что следующее может помочь.

1> GPU имеет много (от сотен до тысяч) ядер низкочастотного потока, что означает, что они должны выполнять одни и те же инструкции. Таким образом, они очень хороши в инструкциях SIMD. Если вы собираетесь вычислять только один элемент матрицы (первый пример и последний), GPU определенно не очень хорош в этом.

2> Для второго теста включите индекс i в выражение, чтобы исключить оптимизацию из компилятора. Или вы можете попробовать изменить 10000 на 50000, чтобы увидеть, есть ли разница.

for i=1:10000
a6=i*(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end    

3> CPU имеет свой собственный векторный процессор (VPU), который также предназначен для SIMD. Единственная проблема в том, что он довольно маленький, от 64 до 256 бит. Так что, если матрица маленькая, процессор гораздо лучше, чем графический процессор. Поэтому, чтобы увидеть выигрыш в производительности GPU, вы можете попробовать большее измерение, скажем, 5000x5000.

Пожалуйста, дайте мне знать, если у вас есть какие-либо дальнейшие результаты по этому вопросу.

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