Пример оптимизации вычислений на CUDA

в 10:42, , рубрики: CUDA, gpgpu, оптимизация, метки: , ,

Введение

Я описываю результаты применения способов оптимизации вычислений на CUDA при моделировании плазмы. Вычисления производятся с использованием Java-привязки к CUDA (JCUDA) [1] на GT630 (Kepler). Моделирование происходит как решение задачи Коши — задание значений параметров в начальный момент времени, затем приращение времени и перерасчет всех уравнений, и т.д. многократно. Вычисления происходят в двойной точности (double). Правильность полученных результатов подтверждена вычислениями на CPU без JCUDA.

Модель параметрической неустойчивости ленгмюровских волн в плазме состоит из уравнений амплитуды, фазы, плотности ионов, напряженности электрического поля (число уравнений каждого типа равно 400), уравнений движения ионов, число которых равно 20000, и двух уравнений накачки. Библиотеки функций (cublas и т.д.) не используются, код уравнений для вычислений на GPU написан руками в .cu файле.

1 Применение основных способов оптимизации вычислений на CUDA

1.1. Важно свести к минимуму передачу данных между памятью GPU и ОЗУ, даже если это означает запуск участков кода на GPU, которые не демонстрируют ускорение по сравнению с CPU [2]. Так, непараллельно происходит вычисление параметров накачки, т.к. они описываются только двумя уравнениями. Их вычисление на CPU происходило бы быстрее, однако требовало бы затрат времени на обмен данными.

1.2. Исходные данные для расчета загружаются в память GPU единожды перед началом моделирования. Далее обмен между ОЗУ и GPU отсутствует кроме моментов времени, результаты которых нужно сохранить.

1.3. Вычисления всех уравнений, кроме расчета накачки, происходят параллельно (для вычисления одного уравнения создается один поток). Подбираются размерности сетки и блока, при которых скорость расчета максимальна. Размерность блока (число потоков в блоке) не должна быть маленькой и большой, по собственному опыту, должна равняться приблизительно числу ядер в GPU (по крайней мере в GPU до 500 ядер).

1.4. Все загружаемые в память GPU данные хранятся в глобальной памяти, но GPU обладает быстрыми типами памяти — разделяемой (shared) и константной (constant). Однако попытки их использовать дали эффект всего 1%.

1.5. В некоторых местах кода много раз выполняются одинаковые вычисления. Например, если присутствует повторяющееся a*b, создается переменная c=a*b, которая затем используется. Подобным образом были сделаны несколько оптимизаций, однако их эффект 1%.

2 Оптимизация использования тригонометрических функций

При вычислении на GPU 85% времени составляет расчет тригонометрических функций, поэтому оптимизация использования тригонометрических функций для данной задачи актуальна.

2.1. Существуют функции sinpi, cospi, однако в модели только 4 функции имеет подходящий вид, а эффект от их использования составляет 2%.

2.2. Также существует функция sincos, одновременно рассчитывающая синус и косинус. Эффект от ее использования составляет 50%. Однако существенным недостатком является необходимость для каждого вычисления выделять память под хранение значений синуса и косинуса, что усложняет ее использование.

2.3. Сделана попытка предварительно рассчитывать синусы и косинусы в каждый момент времени (т.е. создать таблицу значений), а затем использовать посчитанные значения в уравнениях. Четверть функций имеют подобную себе функцию с одинаковым аргументом, однако применение этого способа оптимизации дает эффект до 5%.

2.4. В CUDA каждая мат. функция имеет несколько реализаций, отличающихся по точности – double-функции (sin, cos), float-функции (sinf, cosf), функции пониженной точности (__sinf, __cosf). Так, применение float-функций синуса и косинуса позволяет ускорить вычисление уравнений на 60%, а применение функций синуса и косинуса пониженной точности — на 70%. При этом другие расчеты продолжают выполняться в двойной точности, а точность результатов сохраняется.

Выводы

Время моделирования до оптимизации использования тригонометрических функций составляло 20 минут. Время моделирования до применения пунктов 1.1, 1.2, 1.3 не измерялось, т.к. положения этих пунктов были реализованы изначально.

Время моделирования после оптимизации составляет 7 минут, из них 90% — вычисления на GPU (CUDA), 10% — дополнительные вычисления на CPU, связанные с сохранением результатов (Java), обмен данными между памятью GPU и ОЗУ — 0.01%.

Успешные способы — 1.1, 1.2, 1.3, 2.4 — минимизирован обмен данными между памятью GPU и оперативной памятью, произведен перенос основных вычислений на GPU и их распараллеливание, выбраны оптимальные параметры распараллеливания – размер сетки и блоков, использованы тригонометрические функции пониженной точности.

Способы, от которых пришлось отказаться из-за низкой эффективности или усложнения кода — 1.4, 1.5, 2.1, 2.2, 2.3 — предварительный расчет значений с целью уменьшения числа вычислений, попытки использования быстрых видов памяти GPU.

Результаты применения различных способов оптимизации могут объясняться спецификой моделируемой задачи.

Литература

1. Marco Hutter. JCUDA. jcuda.org.
2. CUDA C Best Practices Guide. NVIDIA Corporation. docs.nvidia.com/cuda/cuda-c-best-practices-guide.

Автор: priymak

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js