Почему 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.
Пожалуйста, дайте мне знать, если у вас есть какие-либо дальнейшие результаты по этому вопросу.