Почему знание модели программирования графических процессоров нескольких поставщиков является необходимым злом… или так оно и есть?

Введение

В моих последних двух постах о параллельном и ускорительном программировании я рассказал об основах ускорительного и параллельного программирования и некоторых концепциях программирования, необходимых для обеспечения корректности вашего кода. Интересный вопрос для программиста, изучающего программирование на ускорителях: Действительно ли мне нужно изучать CUDA, ROCm и SYCL, чтобы программировать для ускорителей NVIDIA, AMD и Intel?

Планируя эту серию статей, я рассчитывал подробно изучить сходство и различия между аппаратным исполнением и моделями памяти каждой архитектуры. Я собирался поговорить об варпах, волновых фронтах и ​​рабочих группах, соответствующих конструкциях, которые CUDA, ROCm и SYCL используют для группового выполнения аппаратных потоков. Однако, чем больше я об этом думаю, тем больше кажется, что это слишком много для поста в блоге. Если вас интересуют эти темы, посмотрите хороший SYCL для разработчиков CUDA от Codeplay.

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

SYCL и графические процессоры нескольких поставщиков

SYCL — это модель программирования, которая поддерживает компилятор для графических процессоров NVIDIA, AMD и Intel. Вы можете написать свой код на SYCL, а затем собрать и запустить его на графических процессорах этих поставщиков. Ранее для этого требовалось использовать различные компиляторы сообщества или создавать компилятор Intel с открытым исходным кодом, в зависимости от вашего целевого графического процессора. С выпуском последних цепочек инструментов Codeplay теперь вы можете быстро и легко запускать свой код с помощью единой предварительно созданной цепочки инструментов. Для получения более подробной информации об этом вы можете прочитать Ruyman Reyes, CTO Codeplay, блог об их новом выпуске.

Один из вопросов, который вы можете задать, заключается в том, могу ли я создать свой код один раз, а затем во время выполнения решить, на каком графическом процессоре он будет работать? Ответ ДА! Это хорошая вещь в этой поддержке, вы создаете свой код один раз и во время выполнения, вы можете выбрать цель вручную или позволить библиотеке времени выполнения выбрать для вас автоматически.

Поскольку вы зашли так далеко, я предполагаю, что это вас интересует, и моя цель — научить вас работать на выбранном графическом процессоре. Для своих целей я тестирую графические процессоры всех трех производителей на Ubuntu 22.04 и выполняю установку через APT.

Установка цепочки инструментов Codeplay

Во-первых, мы устанавливаем некоторые основные системные пакеты:

sudo apt update
sudo apt -y install cmake pkg-config build-essential

Затем мы получаем Intel® oneAPI Base Toolkit 2023.0, который используется цепочкой инструментов Codeplay. Инструкции по тому, как это сделать через APT, можно найти на странице Руководство по установке Intel® oneAPI Toolkits для ОС Linux*. Вот шаги, которые я использовал, но обратитесь к исходной странице, если у вас есть проблемы:

# download the key to system keyring
wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \
| gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null

# add signed entry to apt sources and configure the APT client to use Intel repository:
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list

sudo apt-update
sudo apt install intel-basekit

Специальная настройка поставщика

Убедитесь, что у вас есть базовые драйверы/фреймворки, необходимые для каждого графического процессора, который у вас есть/на который вы хотите ориентироваться, опять же, я ссылаюсь на инструкции Ubuntu:

Установка драйвера графического процессора Intel

Инструкции для графических процессоров Intel на разных ОС можно найти здесь. Поскольку я использую Intel Arc на Ubuntu 22.04, я следовал этим инструкциям.

oneAPI для установки NVIDIA

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

  1. Установите брелок CUDA, CUDA и перезагрузитесь
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.0-1_all.deb
sudo dpkg -i cuda-keyring_1.0-1_all.deb

sudo apt-get update
sudo apt-get install cuda-11-7
sudo apt-get install nvidia-gds
sudo reboot

2. Установите пути, чтобы убедиться, что мой терминал может найти CUDA:

export PATH=/usr/local/cuda-11.7/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-11.7/lib64\
                         ${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

3. Загрузите установщик oneAPI для графических процессоров NVIDIA с сайта Codeplay здесь.

4. Запустите скрипт установки

sh oneapi-for-nvidia-gpus-2023.0.0-linux.sh

5. Перед каждой сборкой в ​​вашем терминале или в вашем .profile убедитесь, что среда сборки настроена правильно, чтобы включить oneAPI и найти CUDA.

. /opt/intel/oneapi/setvars.sh --include-intel-llvm
export PATH=/usr/local/cuda-11.7/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-11.7/lib64\
                         ${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

Установка oneAPI для AMD (бета)

Вы можете перейти здесь, чтобы получить последние официальные инструкции по установке ROCm, снова вставив сюда, чтобы вам было немного проще:

  1. Установите программу AMD amdgpu-install через APT.
sudo apt-get update
wget https://repo.radeon.com/amdgpu-install/5.4.1/ubuntu/jammy/amdgpu-install_5.4.50401-1_all.deb
sudo apt-get install ./amdgpu-install_5.4.50401-1_all.deb

2. Установите последнюю версию драйвера ROCm с помощью программы amdgpu-install.

amdgpu-install --usecase="dkms,graphics,opencl,hip,hiplibsdk"

3. Загрузите установщик oneAPI для графических процессоров AMD (бета) с сайта Codeplay здесь.

4. Запустите скрипт установки

sh oneapi-for-amd-gpus-2023.0.0-linux.sh

5. Перед каждой сборкой в ​​вашем терминале или в вашем .profile убедитесь, что среда сборки настроена правильно, чтобы включить oneAPI и найти ROCm.

. /opt/intel/oneapi/setvars.sh --include-intel-llvm

export PATH=/PATH_TO_ROCM_ROOT/bin:$PATH
export LD_LIBRARY_PATH=/PATH_TO_ROCM_ROOT/lib:$LD_LIBRARY_PATH

Пример кода и поддержка нескольких графических процессоров одновременно

Есть много разных примеров кода, которые вы можете попробовать в репозитории GitHub с открытым исходным кодом, образцы oneAPI:



Из примера Codeplay вы можете видеть, что они создали этот файл simple-sycl-app.cpp:

#include <sycl/sycl.hpp>

int main() {
  // Creating buffer of 4 ints to be used inside the kernel code
  sycl::buffer<sycl::cl_int, 1> Buffer(4);

  // Creating SYCL queue
  sycl::queue Queue;

  // Size of index space for kernel
  sycl::range<1> NumOfWorkItems{Buffer.size()};

  // Submitting command group(work) to queue
  Queue.submit([&](sycl::handler &cgh) {
    // Getting write only access to the buffer on a device
    auto Accessor = Buffer.get_access<sycl::access::mode::write>(cgh);
    // Executing kernel
    cgh.parallel_for<class FillBuffer>(
        NumOfWorkItems, [=](sycl::id<1> WIid) {
          // Fill buffer with indexes
          Accessor[WIid] = (sycl::cl_int)WIid.get(0);
        });
  });

  // Getting read only access to the buffer on the host.
  // Implicit barrier waiting for queue to complete the work.
  const auto HostAccessor = Buffer.get_access<sycl::access::mode::read>();

  // Check the results
  bool MismatchFound = false;
  for (size_t I = 0; I < Buffer.size(); ++I) {
    if (HostAccessor[I] != I) {
      std::cout << "The result is incorrect for element: " << I
                << " , expected: " << I << " , got: " << HostAccessor[I]
                << std::endl;
      MismatchFound = true;
    }
  }

  if (!MismatchFound) {
    std::cout << "The results are correct!" << std::endl;
  }

  return MismatchFound;
}

Сборка под конкретный GPU

Как правило, для сборки для наших различных целей мы могли бы использовать командные строки, которые выглядят следующим образом:

NVIDIA:

clang++ -fsycl -fsycl-targets=nvptx64-nvidia-cuda simple-sycl-app.cpp -o simple-sycl-app

AMD:

clang++ -fsycl -fsycl-targets=amdgcn-amd-amdhsa -Xsycl-target-backend --offload-arch=<ARCH> simple-sycl-app.cpp -o simple-sycl-app

Интел:

clang++ -fsycl -fsycl-targets=spir64 simple-sycl-app.cpp -o simple-sycl-app

Затем я бы просто запустил ./simple-sycl-app и, предполагая, что у меня есть правильный графический процессор в моей системе, мое приложение будет работать. Конечно, это здорово, но я, вероятно, мог бы сделать то же самое, что CUDA/ROCm сами по себе, используя модели программирования, специфичные для конкретного поставщика.

Сборка для графических процессоров нескольких поставщиков

Допустим, я хочу работать с графическими процессорами NVIDIA и Intel одновременно, я действительно могу создать толстый двоичный файл, который будет работать на графическом процессоре NVIDIA или Intel. Хитрость здесь заключается в том, чтобы правильно настроить параметры командной строки сборки, чтобы иметь несколько целей SYCL.

clang++ -fsycl -fsycl-targets=nvptx64-nvidia-cuda,spir64 simple-sycl-app.cpp -o simple-sycl-app

Вы можете видеть, что я просто установил -fsycl-targets для обоих GPU NVIDIA и Intel, разделив мою цель запятой. Теперь, когда я запускаю свою программу, я могу сделать что-то вроде этого:

tonym@LianLi-Linux:~/dev/2023.0Test$ clang++ -fsycl -fsycl-targets=nvptx64-nvidia-cuda,spir64 simple-sycl-app.cpp -o simple-sycl-app
 
tonym@LianLi-Linux:~/dev/2023.0Test$ SYCL_DEVICE_FILTER=cuda SYCL_PI_TRACE=1 ./simple-sycl-app
SYCL_PI_TRACE[basic]: Plugin found and successfully loaded: libpi_cuda.so [ PluginVersion: 11.15.1 ]
SYCL_PI_TRACE[all]: Selected device: -> final score = 1500
SYCL_PI_TRACE[all]:   platform: NVIDIA CUDA BACKEND
SYCL_PI_TRACE[all]:   device: NVIDIA GeForce RTX 3080 Ti
The results are correct!
tonym@LianLi-Linux:~/dev/2023.0Test$
 
tonym@LianLi-Linux:~/dev/2023.0Test$ SYCL_DEVICE_FILTER=ext_oneapi_level_zero:gpu:0 SYCL_PI_TRACE=1 ./simple-sycl-app
SYCL_PI_TRACE[basic]: Plugin found and successfully loaded: libpi_level_zero.so [ PluginVersion: 11.15.1 ]
SYCL_PI_TRACE[all]: Selected device: -> final score = 1550
SYCL_PI_TRACE[all]:   platform: Intel(R) Level-Zero
SYCL_PI_TRACE[all]:   device: Intel(R) Graphics [0x56a0]
The results are correct!

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



Будущая работа

Некоторые проницательные читатели могут заметить, что я не включил пример AMD в свой пример GPU от разных поставщиков. Это связано с тем, что я недавно заменил графический процессор AMD 6800XT на совершенно новый графический процессор AMD RX 7900XT. К сожалению, в настоящее время ROCm не устанавливается должным образом в моей системе Linux, независимо от ядра, которое я использую с картой RDNA3. Как только это будет обновлено, я вернусь и предоставлю инструкции с примером, работающим на всех трех картах.

Обратите внимание, что ранее версия этого потока работала для 6800XT, но это было до того, как программное обеспечение Codeplay было выпущено с использованием потока DPC++ с открытым исходным кодом, задокументированного здесь:



Заключение

OneAPI для графических процессоров NVIDIA от Codeplay позволил мне легко создавать двоичные файлы для графических процессоров NVIDIA или Intel. Время установки дополнительного oneAPI для графических процессоров NVIDIA составило около 10 минут сверх времени, затрачиваемого на установку набора Intel oneAPI Base Toolkit. Это включало установку CUDA и программного обеспечения Codeplay.

Новое программное обеспечение Codeplay открывает совершенно новые возможности для частных лиц и компаний, рассматривающих варианты графических процессоров от разных поставщиков. До того, как варианты были с открытым исходным кодом, компиляторы поддерживались сообществом. Это фантастический вариант, который я обычно использую для повседневной работы.

Однако для людей, рассматривающих долгосрочную поддержку, т. е. предприятий, которые полагаются на цепочки инструментов для получения средств к существованию, официально поддерживаемый вариант от Codeplay и Intel может помочь обеспечить душевное спокойствие.

Если вы хотите узнать, какие технические новости я читаю, вы можете подписаться на меня в Твиттере. Кроме того, посмотрите Code Together, подкаст Intel для разработчиков, который я веду, где мы обсуждаем технологии.

Тони — архитектор программного обеспечения и технический евангелист в Intel. Он был архитектором Intel VTune Profiler и других инструментов для повышения производительности, а совсем недавно руководил командой разработчиков программного обеспечения, которая создала платформу центра обработки данных, которая позволила использовать масштабируемое решение Habana MLPerf.