Что такое программирование GPU?

В мире высокопроизводительных вычислений GPU (графические процессоры) встречаются повсюду, и учёные, работающие в определённых областях, сталкиваются со всё более сложной задачей. А именно: им нужно дорабатывать свои компьютерные программы, чтобы максимально использовать эти современные GPU-системы. Эта задача важна для того, чтобы раскрыть всю мощь этих высокотехнологичных архитектур GPU. 

В этой статье мы дадим базовое понимание программирования GPU тем, кто ищет ответ на вопрос «Что такое программирование GPU?». Кроме того, мы рассмотрим разные подходы и фреймворки для GPU, а также самую популярную платформу программирования GPU на Python, CUDA, и её значение для написания кода для GPU.

Что такое GPU?

Графический процессор (GPU) - это электронная схема, которая ускоряет создание изображений для вывода на экран за счёт быстрой работы с памятью и одновременной обработки данных. Сегодня такие GPU встречаются повсеместно: они есть в различных вычислительных устройствах, таких как персональные компьютеры, игровые консоли, профессиональные рабочие станции и даже смартфоны. 

Со временем GPU превзошли CPU по производительности: у них больше транзисторов, и они отлично справляются с параллельными вычислениями. Превратившись в сложные устройства с собственной памятью, шинами и процессорами, GPU можно рассматривать как дополнительный мозг или процессор компьютерной системы.

Что такое программирование GPU?

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

В чём разница между GPU и CPU?

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

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

В архитектуре GPU приоритет отдаётся обработке данных, а не кешированию данных и управлению потоком выполнения. При параллельном решении задачи возникают два заметных преимущества: во-первых, для каждого элемента выполняется одна и та же операция, что снижает потребность в сложном управлении потоком выполнения; во-вторых, большой набор данных и высокая арифметическая интенсивность снижают потребность в памяти с низкой задержкой.

Какие подходы или API для программирования GPU используются?

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

Существуют различные программные среды и API для программирования GPU. Их делят на модели на основе директив, непереносимые модели на основе ядер и переносимые модели на основе ядер, а также на высокоуровневые фреймворки и библиотеки.

Стандартные C++/Fortran

Программы на стандартных C++ и Fortran могут использовать GPU NVIDIA без внешних библиотек. Компиляторы NVIDIA SDK оптимизируют код для выполнения на GPU, как показано в приведённых рекомендациях.

Программирование на основе директив

Подходы на основе директив предполагают аннотирование существующего последовательного кода, чтобы обозначить циклы и участки для выполнения на GPU. Этот метод, примерами которого являются OpenACC и OpenMP, ориентирован на продуктивность разработки и простоту использования, но может несколько уступать в производительности.

OpenACC: консорциум, созданный в 2010 году. OpenACC стремится предоставить стандартную, переносимую и масштабируемую модель программирования для ускорителей, включая GPU. Изначально он поддерживал только GPU NVIDIA, но расширяет поддержку на другие устройства и архитектуры.

OpenMP: изначально это был API для параллельного программирования с общей памятью для многоядерных CPU, а теперь OpenMP поддерживает перенос вычислений на GPU и ориентирован на разные типы GPU.

Непереносимые модели на основе ядер (нативные модели программирования)

Прямое программирование GPU даёт разработчикам больше контроля благодаря низкоуровневому коду, взаимодействующему непосредственно с GPU. Примеры: CUDA (NVIDIA) и HIP (AMD), причём HIP разработан для совместимости между GPU NVIDIA и AMD.

Переносимые модели на основе ядер (кроссплатформенные экосистемы переносимости)

Кроссплатформенные экосистемы переносимости, такие как Alpaka, Kokkos, OpenCL, RAJA и SYCL, предлагают абстракции более высокого уровня для удобного и переносимого программирования GPU. Их цель - переносимость производительности при едином исходном коде приложения.

Kokkos: разработанная в Sandia National Laboratories, Kokkos - это экосистема на основе C++, поддерживающая эффективные и масштабируемые параллельные приложения на CPU, GPU и FPGA.

OpenCL: открытый стандартный API для параллельных вычислений общего назначения на различном оборудовании. OpenCL предоставляет низкоуровневый программный интерфейс для программирования GPU. Он поддерживает CPU, GPU и FPGA от многих производителей.

SYCL: бесплатная (без лицензионных отчислений) модель программирования на C++ с открытым стандартом. SYCL предоставляет высокоуровневую модель программирования с единым исходным кодом для гетерогенных систем, включая GPU. Её можно реализовать поверх OpenCL, CUDA или HIP.

Поддержка в высокоуровневых языках

Python поддерживает программирование GPU с помощью нескольких библиотек, у каждой из которых своё назначение:

CuPy

  • Библиотека массивов данных на основе GPU.
  • Бесшовная интеграция с NumPy/SciPy.
  • Простой переход к вычислениям на GPU: достаточно заменить 'numpy' и 'scipy' на 'cupy' и 'cupyx.scipy' в коде Python.

cuDF (RAPIDS)

  • Часть RAPIDS, реализующая функциональность CUDA с привязками к Python.
  • Предназначена для работы с датафреймами на GPU.
  • Предлагает API, похожий на pandas, что позволяет пользователям, знакомым с Pandas, ускорить работу без глубоких знаний CUDA.

PyCUDA

  • Среда программирования на Python для CUDA.
  • Предоставляет доступ к CUDA API от NVIDIA из Python.
  • Ограничена GPU NVIDIA и требует опыта программирования на CUDA.

Numba

  • Позволяет выполнять JIT-компиляцию (Just-In-Time) кода Python для выполнения на GPU.
  • Поддерживает GPU NVIDIA, а в будущем ожидается поддержка GPU AMD.

Эти библиотеки предоставляют разнообразные варианты программирования GPU на Python с учётом разных предпочтений и требований пользователей. Будь то переход с NumPy/SciPy с помощью CuPy или использование похожего на pandas API cuDF, Python обеспечивает гибкость для эффективных вычислений с ускорением на GPU.

Что такое CUDA?

CUDA - это платформа параллельных вычислений и API, созданные NVIDIA, с которых началась эра массовых фреймворков для программирования GPU. Эта технология позволяет разработчикам писать код, похожий на C++, который выполняется на GPU.

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

Как установить CUDA на Ubuntu 20.04 для программирования GPU

Вы можете установить CUDA на Ubuntu 20.04, выполнив приведённые ниже пошаговые инструкции:

Шаг 1: сначала скачайте файл конфигурации приоритета CUDA (cuda-ubuntu2004.pin), необходимый для настройки репозиториев CUDA на Ubuntu 20.04, с помощью команды ‘wget’ или ‘curl’.

$ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin

Шаг 2: переместите скачанный файл конфигурации приоритета в указанный каталог, установив для него приоритет 600.

$ sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600

Шаг 3: скачайте пакет репозитория CUDA:

wget https://developer.download.nvidia.com/compute/cuda/11.4.2/local_installers/cuda-repo-ubuntu2004-11-4-local_11.4.2-470.57.02-1_amd64.deb

Шаг 4: установите скачанный пакет репозитория CUDA с помощью менеджера пакетов dpkg.

$ sudo dpkg -i cuda-repo-ubuntu2004-11-4-local_11.4.2-470.57.02-1_amd64.deb

Шаг 5: добавьте публичный ключ, необходимый для аутентификации репозитория CUDA.

$ sudo apt-key add /var/cuda-repo-ubuntu2004-11-4-local/7fa2af80.pub

Шаг 6: обновите списки пакетов:

$ sudo apt-get update

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

Шаг 7: теперь установите CUDA Toolkit на свою машину с Ubuntu 20.04 с помощью следующей команды:

$ sudo apt-get -y install cuda

Выполнив приведённые выше шаги, вы сможете установить CUDA на машину с GPU, поддерживающим CUDA, под управлением Ubuntu 20.04. Теперь вы можете запускать код для GPU и использовать платформу CUDA на своей Linux-машине.

Заключение

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