Анонс: Использование графических процессоров и технологии CUDA для задач молекулярной динамики

В журнале Вычислительные методы и программирование вышла статья А.С Боярченкова и С.И Поташникова: Использование графических процессоров и технологии CUDA для задач молекулярной динамики. Вот ее аннотация (полные тексты доступны по ссылке):

Рассмотрена параллельная реализация расчета парных межчастичных взаимодействий в методе молекулярной динамики при нулевых граничных условиях на графических процессорах с применением платформы NVIDIA CUDA. Впервые предложена эффективная реализация с использованием третьего закона Ньютона на основе технологии CUDA. Предложены приемы оптимизации кода. На видеокарте NVIDIA GeForce 8800 GTX по сравнению со скалярной версией на процессоре AMD Athlon64 2.1 ГГц достигнуто ускорение до 660 раз для системы из 49152 частиц.

Я бесконечно далек от этой тематики, но из чтения статьи мне показалось, что эффективность (относительно расчетной пиковой производительность) у авторов получилась выше, чем у классиков N-body на CUDA, Харриса и Ниланда (см. GPU Gems 3 или на сайте NVidiaN-Body Simulation).

Вместе с тем, мне кажется несколько удивительным, что при оценке ускорения (660 раз) в качестве второй половинки для сравнения выбран старый процессор и несколько "несравнимая" реализация на нем:

  • GPU-реализация: SIMD и одинарная точность;
  • CPU-реализация: скалярная (это написано явно) и, как я понял, двойная точность.

Если воспользоваться другой оценкой из той же статьи: GPU позволяет на задаче N-body получить ~98% от теоретической эффективности, а CPU - около половины, то мы вправе ожидать на современных процессорах порядка 50 GFLOPS, а на GTX280 - порядка 600, что даст разницу в быстродействии в 12 раз. Тоже немало (учитывая и возможности дешевого масштабирования GPU-систем), но все-таки не 600. И самое интересное тут: что же именно не позволяет CPU получить 100% эффективности: недостаток количества (быстрого) L1-кэша или просто недостаток bandwidh.

Tags: 

Comments

Насчет "98% теоретической

Насчет "98% теоретической производительности" - в статье мы как раз рассматриваем вопрос о некорректности оценки в GFLOPS, принятой в сообществе NBody (там используют завышенные FLOP для некоторых арифметических операций).

Насчет сравнения реализаций с разной точностью: одинарная точность для данной задачи достаточна, а без использования SSE скорость расчетов на CPU с одинарной и двойной точностью в нашем случае не отличалась.

В статье мы сравниваем нашу CUDA-реализацию с 2-мя CPU версиями - с "наиболее близкой" и "наиболее быстрой":
1. С простой C++ реализацией, которая условно эквивалентна, той что запускалась на CUDA (отличия в том, что в CPU-версии использовался "треугольный цикл" с учетом симметрии парных сил, а в CUDA-версии - нет, так как это оказалось неэффективно, см. подробности сравнения в тексте).
2. С SSE оптимизированной версией из работы Нитадори.

При сравнении скорости CUDA реализации с нашей простой C++ версией без SSE, запускаемой на Athlon64 2.1GHz, получается ~660 раз (при этом CUDA-версия выполняет в 2 раза больше вычислений, так как не используется симметрия парных сил).

При сравнении с SSE версией из статьи Нитадори, где для Athlon64 2GHz указана цифра ~4 GFLOPS, т.е. используя их оценку сложности вычислительного ядра в 38 FLOP получаем время расчета парной силы: 38 / 4e+9 = ~1е-8 секунды. А время нашей CUDA версии с эквивалентным парным потенциалом без короткодействующего степенного слагаемого ~7.7e-11 секунды, т.е. ускорение в ~120 раз (также при в 2 раза большем объеме вычислений на GPU).

Для приблизительной оценки реализаций на многоядерных CPU можно поделить время одноядерной на количество ядер, т.е. для 4-х ядерного CPU ускорение от 8800 GTX составит ~30 раз.

Само по себе сравнение

Само по себе сравнение цифирок, особенно если FLOPS-ы не оцениваются очень точно (как оно для матриц) - дурное дело.
Сравнивать, конечно, нужно времена реального исполнения - а для этого придется хорошо спрограммировать SSE4-версию задачи, что при наличии GPU-версии, которая гарантированно быстрее - тоже глупое занятие. Поэтому моя придирка к цифрам - это именно придирка (ведь с VAX вы не стали сравнивать :).
Чтобы эту тему закрыть: в своей оценке ("30 раз") вы не учитываете то, что у современных 4-ядерников по два SSE-блока на ядро (в отличие от Athlon) т.е. они будут еще вдвое быстрее. В реальности для Opteron у меня получалось 3.5 операции на такт (из теоретических четырех), а у Penryn - 7.2 операции (из 8), но реальность была очень простой. А с оценкой "в 15 раз" я согласен, не о чем тут спорить.

Куда интереснее второй вопрос - почему на GPU (гораздо более примитивной архитектуре в расчете на одно вычислительное ядро) получается 98%-эффективность от пиковой, а на CPU - только 50. Это либо креативность подсчета FLOPS-ов, тогда надо пересчитывать эффективность, либо же какие-то более фундаментальные ограничения.
Я вижу, что у GPU (в расчете на всю машинку в целом, а не на отдельный SP) есть такие существенные отличия:
а) Гораздо большая bandwidth памяти. При этом latency несколько (но не на порядки) хуже, но latency успешно прячется оборудованием если исполняемых thread blocks достаточно много. Т.е. процентов 90% от bandwidth удается вполне успешно добыть.
б) Гораздо больше (опять, в расчете на ящик) быстрой памяти: регистров и shared mem. Т.е. на GTX280 имеем 2 мегабайта регистров и мегабайт shared, это много больше чем L1-кэш у CPU.
в) архитектурно "кэш" (точнее, быстрая память) гораздо прямолинейнее - никто не заботится о когерентности (такой задачи просто нет), никто не заботится о prefetch (кроме текстурных кэшей), нет никаких 32-байтных cache lines. Т.е. вся трудность, но очень легко понимаемая - это coalesced access, а никакой умной логики по эмуляции быстрой быстрой IBM XT нету.

Вот собственно самое интересное - это какой фактор играет наиболее существенную роль в том, что CPU показывает свои "50% от теоретической/пиковой" производительности. Может быть просто не доходили руки дооптимизировать это место в той же степени, в какой линейную алгебру оптимизируют?