Для чего драйвера cuda
CUDA: с места в карьер
NB: Статья — краткое введение, покрыть все ньюансы программирования под CUDA в одной статье вряд ли возможно 🙂
О железе
CUDA работает на видеокартых начиная с 8400GS и выше. Разные видеокарты имеют разые возможности. В целом, если вы видите что в видеокарте например 128 SP(Streaming Processor) — это значит что там 8 SIMD MP (multiprocessor), каждый из которых делает одновременно 16 операций. На один MP есть 16кб shared memory, 8192 штуки 4-хбайтных регистров (В картах серии GTX2xx значения больше). Также есть 64кб констант общие для всех MP, они кешируются, при непопадании в кеш — достаточно большая задержка (400-600 тактов). Есть глобальная память видеокарты, доступ туда не кешируется, и текстуры (кешируется, кеш оптимизирован для 2D выборок). Для использования нескольких видеокарт нужно во первый отключать SLI в дровах, а во вторых — на каждую видеокарту запускать по потоку, и вызывать cudaSetDevice().
С чего начать?
Его вы можете использовать во всех своих проектах, только вместо «../../common/inc » можно указать абсолютный путь (или переменную окружения).
nvcc — это и есть великий и ужасный компилатор CUDA. На выходе он генерирует объектный файл, в котором уже включена откомпилированная программа для видеокарты.
Обратите внимение на описание интерфейса в Mandelbrot_kernel.h — тут руками приходится описывать kernel-ы которые мы собираемся вызывать из основной С++ программы (впрочем их обычно не много, так что это не страшно).
После того как вам удалось запустить пример SDK, можно рассмотреть, чем же CUDA программа отличается от обычной.
Определение функций
Определение данных
__constant__ — задает переменную в константной памяти. Следует обратить внимание, что значения для констант нужно загружать функцией cudaMemcpyToSymbol. Константы доступны из всех тредов, скорость работы сравнима с регистрами(когда в кеш попадает).
__shared__ — задает переменную в общей памяти блока тредов (т.е. и значение будет общее на всех). Тут нужно подходить с осторожностью — компилятор агрессивно оптимизирует доступ сюда(можно придушить модификатором volatile), можно получать race condition, нужно использовать __syncthreads(); чтобы данные гарантированно записались. Shared memory разделена на банки, и когда 2 потока одновременно пытаются обратиться к одному банку, возникает bank conflict и падает скорость.
Все локальные переменные которые вы определеили в ядре (__device__) — в регистрах, самая высокая скорость доступа.
Как поток узнает над чем ему работать
Основая идея CUDA в том, что для решения вашей задачи вы запускаете тысячи и тысячи потоков, поэтому не стоит пугаться того что тут будет дальше написано 🙂
Допустим, надо сделать какую-то операцию над картинкой 200×200. Картинка разбивается на куски 10×10, и на каждый пиксел такого кусочка запускаем по потоку. Выглядить это будет так:
dim3 threads(10, 10);//размер квардатика, 10*10
dim3 grid(20, 20);//сколько квадратиков нужно чтобы покрыть все изображение
your_kernel >>(image, 200,200);//Эта строка запустит 40’000 потоков (не одновременно, одновременно работать будет 200-2000 потоков примерно).
В отличии от Brook+ от AMD, где мы сразу определяем какому потоку над какими данными работать, в CUDA все не так: передаваеиые kernel-у параметры одинаковые для всех потоков, и поток должен сам получить данные для себя, чтобы сделать это, потоку нужно вычислить, в каком месте изображения он находится. В этом помогают магические переменные blockDim, blockIdx.
const int ix = blockDim.x * blockIdx.x + threadIdx.x;
const int iy = blockDim.y * blockIdx.y + threadIdx.y;
В ix и iy — координаты, с помощью которых можно получить исходные данные из массива image, и записать результат работы.
Оптимизация
Не получается?
В первую очередь следует прочитать документацию вместе с SDK (NVIDIA_CUDA_Programming_Guide, CudaReferenceManual, ptx_isa), после этого можно спросить на официальном форуме — там даже девелоперы nVidia часто отписываются, да и вообще много умных людей. По русски можно спросить у меня на форуме например, где отвечу я 🙂 Также много людей обитает на gpgpu.ru.
Надеюсь это введение поможет людям, решившим попробовать программирование для видеокарт. Если есть проблемы/вопросы — буду рад помочь. Ну а в переди нас ждет введение в Brook+ и SIMD x86
CUDA: Как работает GPU
Внутренняя модель nVidia GPU – ключевой момент в понимании GPGPU с использованием CUDA. В этот раз я постараюсь наиболее детально рассказать о программном устройстве GPUs. Я расскажу о ключевых моментах компилятора CUDA, интерфейсе CUDA runtime API, ну, и в заключение, приведу пример использования CUDA для несложных математических вычислений.
Вычислительная модель GPU:
При использовании GPU вы можете задействовать грид необходимого размера и сконфигурировать блоки под нужды вашей задачи.
CUDA и язык C:
Дополнительные типы переменных и их спецификаторы будут рассмотрены непосредственно в примерах работы с памятью.
CUDA host API:
Перед тем, как приступить к непосредственному использованию CUDA для вычислений, необходимо ознакомиться с так называемым CUDA host API, который является связующим звеном между CPU и GPU. CUDA host API в свою очередь можно разделить на низкоуровневое API под названием CUDA driver API, который предоставляет доступ к драйверу пользовательского режима CUDA, и высокоуровневое API – CUDA runtime API. В своих примерах я буду использовать CUDA runtime API.
Понимаем работу GPU:
Как было сказано, нить – непосредственный исполнитель вычислений. Каким же тогда образом происходит распараллеливание вычислений между нитями? Рассмотрим работу отдельно взятого блока.
Задача. Требуется вычислить сумму двух векторов размерностью N элементов.
Нам известна максимальные размеры нашего блока: 512*512*64 нитей. Так как вектор у нас одномерный, то пока ограничимся использованием x-измерения нашего блока, то есть задействуем только одну полосу нитей из блока (рис. 3).
Рис. 3. Наша полоса нитей из используемого блока.
Заметим, что x-размерность блока 512, то есть, мы можем сложить за один раз векторы, длина которых N // Функция сложения двух векторов
__global__ void addVector( float * left, float * right, float * result)
<
//Получаем id текущей нити.
int idx = threadIdx.x;
Таким образом, распараллеливание будет выполнено автоматически при запуске ядра. В этой функции так же используется встроенная переменная threadIdx и её поле x, которая позволяет задать соответствие между расчетом элемента вектора и нитью в блоке. Делаем расчет каждого элемента вектора в отдельной нити.
Пишем код, которые отвечает за 1 и 2 пункт в программе:
#define SIZE 512
__host__ int main()
<
//Выделяем память под вектора
float * vec1 = new float [SIZE];
float * vec2 = new float [SIZE];
float * vec3 = new float [SIZE];
//Инициализируем значения векторов
for ( int i = 0; i //Указатели на память видеокарте
float * devVec1;
float * devVec2;
float * devVec3;
…
dim3 gridSize = dim3(1, 1, 1); //Размер используемого грида
dim3 blockSize = dim3(SIZE, 1, 1); //Размер используемого блока
Теперь нам остаеться скопировать результат расчета из видеопамяти в память хоста. Но у функций ядра при этом есть особенность – асинхронное исполнение, то есть, если после вызова ядра начал работать следующий участок кода, то это ещё не значит, что GPU выполнил расчеты. Для завершения работы заданной функции ядра необходимо использовать средства синхронизации, например event’ы. Поэтому, перед копированием результатов на хост выполняем синхронизацию нитей GPU через event.
Код после вызова ядра:
//Выполняем вызов функции ядра
addVector >>(devVec1, devVec2, devVec3);
//Хендл event’а
cudaEvent_t syncEvent;
cudaEventCreate(&syncEvent); //Создаем event
cudaEventRecord(syncEvent, 0); //Записываем event
cudaEventSynchronize(syncEvent); //Синхронизируем event
Рассмотрим более подробно функции из Event Managment API.
Рис. 4. Синхронизация работы основоной и GPU прграмм.
На рисунке 4 блок «Ожидание прохождения Event’а» и есть вызов функции cudaEventSynchronize.
Ну и в заключении выводим результат на экран и чистим выделенные ресурсы.
cudaFree(devVec1);
cudaFree(devVec2);
cudaFree(devVec3);
Думаю, что описывать функции высвобождения ресурсов нет необходимости. Разве что, можно напомнить, что они так же возвращают значения cudaError_t, если есть необходимость проверки их работы.
Заключение
Надеюсь, что этот материал поможет вам понять, как функционирует GPU. Я описал самые главные моменты, которые необходимо знать для работы с CUDA. Попробуйте сами написать сложение двух матриц, но не забывайте об аппаратных ограничениях видеокарты.
CUDA и удалённый GPU
CUDA всем хороша, пока под рукой есть видеокарта от Nvidia. Но что делать, когда на любимом ноутбуке нет Nvidia видеокарты? Или нужно вести разработку в виртуальной машине?
Я постараюсь рассмотреть в этой статье такое решение, как фреймворк rCUDA (Remote CUDA), который поможет, когда Nvidia видеокарта есть, но установлена не в той машине, на которой предполагается запуск CUDA приложений. Тем, кому это интересно, добро пожаловать под кат.
rCUDA (Remote CUDA) — фреймворк, реализующий CUDA API, позволяющий использовать удалённую видеокарту. Находится в работоспособной бета-версии, доступен только под Linux. Основная цель rCUDA — полная совместимость с CUDA API, вам не нужно никак модифицировать свой код, достаточно задать специальные переменные среды.
Что такое rCUDA
rCUDA (Remote CUDA) — фреймворк, реализующий CUDA API, позволяющий использовать для CUDA вычислений видеокарту, расположенную на удалённой машине, не внося никаких изменений в ваш код. Разработан в политехническом университете Валенсии (rcuda-team).
Ограничения
На данный момент поддерживаются только GNU/Linux системы, однако разработчики обещают поддержку Windows в будущем. Текущая версия rCUDA, 18.03beta, совместима с CUDA 5-8, то есть CUDA 9 не поддерживается. Разработчиками заявлена полная совместимость с CUDA API, за исключением графики.
Возможные сценарии использования
Краткая инструкция
Тестовая конфигурация
Тестирование проводилось на следующей конфигурации:
Сервер:
Ubuntu 16.04, GeForce GTX 660
Клиент:
Виртуальная машина с Ubuntu 16.04 на ноутбуке без дискретной видеокарты.
Получение rCUDA
Cамый сложный этап. К сожалению, на данный момент единственный способ получить свой экземпляр этого фреймворка — заполнить соответствующую форму запроса на официальном сайте. Впрочем, разработчики обещают отвечать в течение 1-2 дней. В моём случае мне прислали дистрибутив в тот же день.
Установка CUDA
Важно! На клиенте следует отказаться от установки nvidia драйвера. По умолчанию CUDA Toolkit будет доступен по адресу /usr/local/cuda/. Установите CUDA Samples, они понадобятся.
Установка rCUDA
Распакуем полученный от разработчиков архив в нашу домашнюю директорию на сервере и на клиенте.
Проделать эти действия нужно как на сервере, так и на клиенте.
Запуск демона rCUDA на сервере
Настройка клиента
Откроем на клиенте терминал, в котором в дальнейшем будем запускать CUDA код. На стороне клиента нам необходимо «подменить» стандартные библиотеки CUDA на библиотеки rCUDA, для чего добавим соответствующие пути в переменную среды LD_LIBRARY_PATH. Также нам необходимо указать количество серверов и их адреса (в моём примере он будет один).
Сборка и запуск
Попробуем собрать и запустить несколько примеров.
Пример 1
Начнём с простого, с deviceQuery — примера, который просто выведет нам параметры CUDA совместимого устройства, то есть в нашем случае удалённого GTX660.
Важно! Без EXTRA_NVCCFLAGS=—cudart=shared чуда не получится
Замените на путь, который вы указали для CUDA Samples при установке CUDA.
Запустим собранный пример:
Если вы всё сделали правильно, результат будет примерно таким:
Самое главное, что мы должны увидеть:
Device0 = GeForce GTX 660
Result = PASS
Отлично! Нам удалось собрать и запустить CUDA приложение на машине без дискретной видеокарты, использовав для этого видеокарту, установленную на удалённом сервере.
Важно! Если вывод приложения начинается со строк вида:
значит необходимо добавить на сервере и на клиенте в файл «/etc/security/limits.conf» следующие строки:
Таким образом, вы разрешите всем пользователям (*) неограниченное (unlimited) блокирование памяти (memlock). Еще лучше будет заменить * на нужного пользователя, а вместо unlimited подобрать менее жирные права.
Пример 2
Теперь попробуем что-то поинтереснее. Протестируем реализацию скалярного произведения векторов с использованием разделяемой памяти и синхронизации («Технология CUDA в примерах» Сандерс Дж. Кэндрот Э. 5.3.1).
В данном примере мы рассчитаем скалярное произведение двух векторов размерностью 33 * 1024, сравнивая ответ с результатом, полученным на CPU.
Такой результат говорит нам, что всё у нас хорошо:
Пример 3
Запустим еще один стандартный тест CUDA- matrixMulCUBLAS (перемножение матриц).
[Matrix Multiply CUBLAS] — Starting…
GPU Device 0: «GeForce GTX 660» with compute capability 3.0
MatrixA(640,480), MatrixB(480,320), MatrixC(640,320)
Computing result using CUBLAS. done.
Performance= 436.24 GFlop/s, Time= 0.451 msec, Size= 196608000 Ops
Computing result using host CPU. done.
Comparing CUBLAS Matrix Multiply with CPU results: PASS
NOTE: The CUDA Samples are not meant for performance measurements. Results may vary when GPU Boost is enabled.
Performance= 436.24 GFlop/s,
Comparing CUBLAS Matrix Multiply with CPU results: PASS
Безопасность
Я не нашёл в документации к rCUDA упоминания о каком-либо способе авторизации. Думаю, на данный момент самое простое, что можно сделать, это открыть доступ к нужному порту (8308) только с определённого адреса.
При помощи iptables это будет выглядеть так:
В остальном оставляю вопрос безопасности за рамками данного поста.
NVIDIA CUDA-X
Библиотеки GPU-ускорения для ИИ и HPC
Разработчики, исследователи и изобретатели во всех отраслях используют программирование на GPU для ускорения своих приложений. Разработка таких приложений требует надежной среды программирования с высокооптимизированными библиотеками для конкретных областей. NVIDIA CUDA-X создана на основе CUDA® и представляет собой набор библиотек, инструментов и технологий, которые обеспечивают значительно более высокую производительность по сравнению с другими решениями во всех отраслях: от ИИ до НРС.
Ускорение современных приложений
Библиотеки CUDA-X AI и CUDA-X HPC эффективно используют мощность графических процессоров NVIDIA с тензорными ядрами для ускорения разработки и развертывания приложений в различных отраслях.
Cuda-X AI
Cuda-X HPC
Современные технологии ИИ способны кардинально изменить работы во многих отраслях, но применение их возможностей представляет собой сложную задачу. Разработка приложений на базе ИИ осуществляется в несколько этапов: обработка данных, конструирование признаков, машинное обучение, верификация и развертывание. Каждый шаг включает в себя обработку крупных массивов данных и осуществление больших объемов вычислений. CUDA-X AI предоставляет инструменты и технологии, необходимые решения этой задачи.
Приложения для НРС используются во многих отраслях: от гидродинамики до моделирования погоды. CUDA-X HPC – это набор библиотек, инструментов, компиляторов и API, с помощью которых разработчики решают самые сложные в мире задачи. CUDA-X HPC включает в себя настроенные ядра для высокопроизводительных вычислений (HPC). Библиотеки GPU-ускорения для линейной алгербры, параллельных алгоритмов, обработки сигналов и изображений создают основу для приложений, требующих большого объема вычислений, в таких областях, как вычислительная физика, химия, молекулярная динамика и сейсмические исследования.
Расширенная доступность
Платформа CUDA-X имеет расширенную экосистему и доступна для использования. Библиотеки программного ускорения являются частью основных облачных платформ, в том числе AWS, Microsoft Azure и Google Cloud. Фрагменты кода или контейнеризированные программные стеки можно бесплатно скачать из репозитория NGC. С помощью GPU NVIDIA библиотеки CUDA-X можно разворачивать на любых устройствах: ПК, рабочих станциях, серверах, суперкомпьютерах, облачных платформах и IoT-устройствах.
Более миллиона разработчиков используют CUDA-X для повышения продуктивности благодаря постоянному росту производительности приложений. Если вы создаете новое приложение или стремитесь ускорить его работу, CUDA-X обеспечивает самый эффективный способ.
Используйте мощность GPU для приложений
Узнайте, как NVIDIA CUDA-X и в частности библиотеки GPU-ускорения трансформируют рабочие процессы в отраслях.
О технологии многопотоковых вычислений CUDA в видеокартах компании Nvidia
Видеокарты производства компании Nvidia пользуются заслуженной славой в области проведения надежных высокопроизводительных вычислений. Благодаря наличию аппаратных возможностей технологии CUDA, «зеленые карты» показывают отличные результаты и при майнинге на большинстве алгоритмов консенсуса PoW.
Рассмотрим подробнее некоторые особенности CUDA.
Что такое технология CUDA?
CUDA (Compute Unified Device Architecture) — это технология многопотоковых компьютерных вычислений, созданная компанией NVIDIA. Она позволяет значительно увеличить производительность при проведении сложных расчетов за счет распараллеливания на множестве вычислительных ядер.
Приложения CUDA используются для обработки видео и аудио, моделирования физических эффектов, в процессе разведки месторождений нефти и газа, проектировании различных изделий, медицинской визуализации и научных исследованиях, в разработке вакцин от болезней, в том числе COVID-19, физическом моделировании и других областях.
CUDA ™ — это архитектура параллельных вычислений общего назначения, которая позволяет решать сложные вычислительные задачи с помощью GPU. CUDA поддерживает операционные системы Linux и Windows. Чем больше ядер CUDA имеет видеокарта и чем больше частота их работы, тем большую производительность она может обеспечить.
Каждая дополнительна единица вычислительной мощности требует соответствующего количества потребленной электроэнергии. Чем меньший технологический процесс используется при производстве вычислительных ядер, тем меньшие напряжения используются для их питания и, соответственно снижается потребление. Поэтому, даже если видеокарты разных поколений имеют одинаковую теоретическую вычислительную мощность в TFlops, их эффективность кардинально отличается по КПД, в значительной мере зависящему от потребления полупроводниковых элементов, из которых состоят ядра видеопроцессоров.
Архитектура CUDA упрощенно включает набор исполняемых команд и аппаратный механизм проведения параллельных вычислений внутри графического процессора. Разработчики программного обеспечения, в том числе майнеров, для работы с CUDA обычно используют языки программирования высокого уровня (C, Фортран). В будущем в CUDA планируется добавление полноценной поддержки C ++, Java и Python. Продвинутые программисты дополнительно улучшают эффективность майнеров с помощью оптимизации кода майнеров на языке более низкого (машинного) уровня – Ассемблере. В качестве примера в этом контексте можно привести Клеймор дуал майнер, который показывает высочайшую эффективность на зеленых видеокартах.
В технологии CUDA есть три важных элемента: библиотеки разработчика, среда выполнения и драйвера. Все они прямо влияют на производительность и надежность работы приложений.
Драйвер — это уровень абстракции устройств с поддержкой CUDA, который обеспечивает интерфейс доступа для аппаратных устройств. С помощью среды выполнения через этот уровень реализуется выполнение различных функций по проведению сложных вычислений.
Таблица версий CUDA, поддерживающихся в драйверах NVIDIA разных версий:
Версия CUDA | Linux x86_64 | Windows x86_64 |
---|---|---|
CUDA 11.1 | >=455.23 | >=456.38 |
CUDA 11.0.3 Update 1 | >= 450.51.06 | >= 451.82 |
CUDA 11.0.2 GA | >= 450.51.05 | >= 451.48 |
CUDA 11.0.1 RC | >= 450.36.06 | >= 451.22 |
CUDA 10.2.89 | >= 440.33 | >= 441.22 |
CUDA 10.1 (10.1.105) | >= 418.39 | >= 418.96 |
CUDA 10.0.130 | >= 410.48 | >= 411.31 |
CUDA 9.2 (9.2.148 Update 1) | >= 396.37 | >= 398.26 |
CUDA 9.2 (9.2.88) | >= 396.26 | >= 397.44 |
CUDA 9.1 (9.1.85) | >= 390.46 | >= 391.29 |
CUDA 9.0 (9.0.76) | >= 384.81 | >= 385.54 |
CUDA 8.0 (8.0.61 GA2) | >= 375.26 | >= 376.51 |
CUDA 8.0 (8.0.44) | >= 367.48 | >= 369.30 |
CUDA 7.5 (7.5.16) | >= 352.31 | >= 353.66 |
CUDA 7.0 (7.0.28) | >= 346.46 | >= 347.62 |
Для CUDA 6.5 нужны драйвера 340.0+, для CUDA 6.0 — 331.00, для CUDA 5.5 — не ниже 319.00.
При установке новых драйверов на видеокарты со старой версией compute capability вычисления производиться не будут.
Например, на большинство видеокарт с архитектурой Kepler (GeForce 640 — 780Ti, 910M, GTX TITAN, compute capability 3.5) нет смысла ставить драйвера новее 441.22 (Windows) или 440.33 (Linux), так как в них отсутствует поддержка compute capability 3.x.
Информация, которую нужно учитывать при установке драйверов для видеокарт Nvidia на предмет соответствия версии compute capability CUDA:
Библиотеки разработки (CUDA SDK) на практике реализуют выполнение математических операций и крупномасштабных задач параллельных вычислений.
Среда выполнения CUDA — это интерфейс разработчика плюс компоненты выполнения программного кода. Она определяет основные типы данных и функций для проведения вычислений, преобразований, управления памятью, позволяет реализовать доступ к устройствам и спланировать выполнение команд.
Программный код CUDA на практике обычно состоит из двух частей, одна из которых выполняется на CPU, а другая на GPU.
Ядро CUDA имеет три важных абстрактных понятия:
которые могут быть достаточно легко представлены и использованы на языке программирования Си.
Программный стек CUDA состоит из нескольких уровней, аппаратного драйвера, интерфейса прикладного программирования (API) и среды его выполнения, а также двух расширенных математических библиотек общего назначения, CUFFT и CUBLAS.
Теоретически каждое новое поколение CUDA должно демонстрировать более высокую производительность за счет устранения выявленных ошибок, оптимизации кода, добавления новых алгоритмов и прочих новшеств. К сожалению, на практике это не всегда соответствует реалиям. В особенности это связано с постоянным ростом аппетита программ по отношению аппаратным ресурсам. Это касается не только программных пакетов CUDA, но затрагивает даже такие, казалось бы, независимые операционные системы, как Linux.
Влияет ли на хешрейт версия CUDA, установленная на компьютере?
Практические опыты с майнерами на разных версиях CUDA показывают, что новые версии особого прироста в хешрейте не дают.
Использование новых драйверов Nvidia обычно сопряжено с увеличением требований к аппаратному обеспечению и часто влечет рост потребления видеопамяти, что не всегда положительно сказываются на производительности видеокарт при майнинге.
Это особенно проявляется в быстродействии и потреблении видеопамяти при майнинге на алгоритме Ethash/DaggerHashimoto. Как правило, старые версии драйверов потребляют меньше видеопамяти при одинаковой производительности на Ethash.
Для обычных пользователей нет необходимости заботиться о версии CUDA, если только этого не требуют последние версии майнеров с новыми поддерживающимися алгоритмами.
Тем не менее, нужно учитывать, что технология CUDA постоянно совершенствуется, в нее добавляются новые возможности, которые требуют адаптации программ-майнеров. Поэтому современные майнеры иногда имеют разные версии, которые поддерживают работу с разными версиями CUDA 8.0, 9.1/9.2, а также 10.0, 10.1 и 10.2.