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

CUDA от NVIDIA обеспечивает массивно-параллельную архитектуру для графических процессоров, которую можно использовать для численных вычислений. В то время как типичный процессор Intel общего назначения может иметь 4 или 8 ядер, графический процессор NVIDIA может иметь тысячи ядер CUDA и конвейер, который поддерживает параллельную обработку тысяч потоков, что значительно ускоряет обработку. Это можно использовать для значительного сокращения времени обучения в приложениях машинного обучения, что увеличивает количество экспериментов и итераций, которые можно запускать при настройке модели.

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

В приведенном ниже примере я продемонстрировал, как это можно сделать с помощью Python, не требуя глубоких знаний CUDA и его тонкостей. Для этого примера я предлагаю использовать дистрибутив Anaconda Python, который упрощает управление различными средами Python. Следуйте инструкциям по загрузке и установке Anaconda, приведенным здесь для вашей конкретной операционной системы.

В этом примере мы будем использовать Numba и NumPy для моделирования простой операции, которая может выполняться параллельно с использованием ядер ЦП, а также ядер CUDA.

Вам также понадобится Anaconda Accelerate, который представляет собой набор пакетов для крупномасштабной обработки данных, который также поддерживает CUDA для параллелизма. Чтобы отключить Accelerate для Anaconda вместе с его зависимостями, выполните в среде conda следующее:

$ conda install accelerate

Вот простая программа, которая создает два вектора со 100 миллионами элементов, которые генерируются случайным образом. Затем выполняется вычисление, так что каждая запись из одного вектора возводится в степень соответствующей записи в другом и сохраняется в третьем векторе, который возвращается в качестве результатов вычисления. Программируя это линейно, мы будем использовать цикл for для выполнения этого вычисления и возврата ответа. Мы добавили сюда код времени, чтобы узнать, сколько времени это займет.

На Intel Core i5 эта программа занимает около 35 секунд.

Мы берем ту же программу и немного модифицируем ее для параллелизма с CUDA.

Строка 3. Импортируйте пакет numba и декоратор векторизации.

Строка 5: Декоратор векторизации функции pow заботится о распараллеливании и сокращении функции на нескольких ядрах CUDA. Для этого он компилирует Python в машинный код при первом вызове и запускает его на графическом процессоре. Декоратор векторизации принимает в качестве входных данных сигнатуру функции, которая должна быть ускорена, вместе с целью для генерации машинного кода. В этом случае «cuda» означает, что машинный код генерируется для графического процессора. Он также поддерживает целевые «cpu» для однопоточного процессора и «параллельный» для многоядерных процессоров. Разработчики могут использовать их для распараллеливания приложений даже при отсутствии графического процессора на стандартных многоядерных процессорах, чтобы извлечь максимум производительности и эффективно использовать дополнительные ядра. И все это без изменений кода.

Строка 6: Затем мы преобразуем функцию pow для работы со скаляром, а не со всем вектором. Это будет атомарная задача, выполняемая одним из ядер CUDA, распараллеливание которого выполняет Numba.

Строка 16: вызов функции изменяется для получения третьего вектора вместо передачи его в качестве параметра.

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

То, что раньше занимало 35 секунд, теперь занимает всего 0,36 секунды на GeForce GTX 1050Ti. Это почти 100-кратное улучшение с минимальными изменениями.

После смерти закона Мура разработчикам приходится все больше полагаться на параллельные вычисления для увеличения производительности приложений и оптимального использования доступного оборудования. Фреймворки высокого уровня с поддержкой таких технологий, как CUDA, упрощают, как никогда, разблокировку суперкомпьютера, который находится на вашем рабочем столе. Разобравшись с этим, давайте создадим что-нибудь полезное.