Ruda Руководство по программированию
English | 简体中文 | 日本語 | Deutsch | Русский
Документация · Среда выполнения API · Вычислительные библиотеки · 中文
1. Хост и устройство
Код хоста Rust выбирает устройства, подготавливает входные данные, создает аргументы и считывает результаты. Ядра устройств описывают параллельные вычисления. Интерфейс расширяет их до IR для внутренней компиляции и выполнения.
ruda-kernel::dsl — это интерфейс ядра общего назначения. Ядра используют синтаксис Rust с типами, макросами и операциями внешнего интерфейса; произвольные программы Rust и код стандартной библиотеки не могут быть просто скомпилированы в GPU.
Тензорная платформа распределяет операции по вычислительным библиотекам. Приложениям не нужно писать ядра на уровне потоков, чтобы использовать умножение матриц.
2. Иерархия выполнения
| Ruda концепция | Цель |
|---|---|
RudaCount |
Количество рабочих групп при запуске |
RudaDim |
Параметры выполнения каждой рабочей группы |
ABSOLUTE_POS |
Глобальная позиция в одномерном поэлементном ядре |
Array<T> |
Доступ к одномерным массивам в ядрах |
Tensor<T> |
Доступ к тензору ядра с метаданными формы и шага |
Runtime |
Компилятор Associates, вычислительный сервер и типы устройств |
Соответствующие концепции CUDA см. в руководстве по совместимости. Публичный экспорт находится в DSL prelude.
3. Ваше первое ядро
Это ядро взято из примера времени выполнения ptx, который включает в себя полную хост-программу и проверки выполнения:
use ruda_kernel::dsl::prelude::*;
#[ruda(launch)]
fn add(a: &Array<f32>, b: &Array<f32>, output: &mut Array<f32>) {
if ABSOLUTE_POS < output.len() {
output[ABSOLUTE_POS] = a[ABSOLUTE_POS] + b[ABSOLUTE_POS];
}
}
Импортируйте макрос и введите его с помощью ruda_kernel::dsl::prelude::*. Выходная длина исключает излишки хвостовой резьбы; оба входных массива должны содержать как минимум столько же элементов, сколько и выходной.
В примере запускается 64 исполнительных блока на каждую рабочую группу и округляется число рабочих групп в большую сторону. Это конфигурация примера, а не универсально оптимальный размер ядра.
4. Память и аргументы
Используйте ComputeClient для создания или выделения буферов устройств, а затем создайте аргументы ядра из их дескрипторов. Отличайте количество байтов от количества элементов:
client.empty(size)принимает размер в байтах.- В примере
ArrayArg::from_raw_parts(handle, count)count— это количество элементов массива. RudaTensor<R>содержит дескриптор хранения, форму, шаги, dtype, устройство и параметры квантования.
Клонирование дескриптора или тензора не копирует базовые данные устройства. Чтобы изменить макет, используйте соответствующую операцию непрерывного преобразования, копирования или преобразования. Редактирование метаданных само по себе не приводит к перераспределению хранилища.
5. Отправка, возврат и синхронизация
Отправка ядра и доступность результатов — это отдельные этапы. Завершение отправки хоста не определяет время или успех выполнения устройства.
read_one ожидает обратного чтения и возвращает Result; read_async предоставляет асинхронные результаты. sync() возвращает будущее, которого следует ожидать. flush() отправляет команды в очереди; это не заменяет чтение результата.
Несколько потоков, обращающихся к одним и тем же данным, должны учитывать зависимости производителя и потребителя. set_stream небезопасен, и само по себе время жизни переменных хоста не определяет завершение задачи устройства.
6. Границы безопасности
Типы, владение и заимствование ограничивают ресурсы и интерфейсы хоста. Обертки низкого уровня также должны поддерживать требования к выполнению устройства:
- Диапазоны хранения аргументов, dtype, выравнивание и макет соответствуют обращениям к ядру.
- Данные, используемые асинхронными задачами, остаются действительными до завершения.
- Общие записи между потоками и потоками синхронизированы правильно.
- Вызывающие объекты, создающие необработанные аргументы или использующие неконтролируемые запуски, удовлетворяют своим контрактам безопасности.
Проверенный запуск не является полным доказательством безопасности произвольного ядра. В примере используются явные блоки unsafe с пояснениями безопасности для создания и запуска необработанных аргументов. См. Время выполнения API.
7. От ядер к вычислительным библиотекам
Используйте ruBLAS, ruDNN и ruPRIM для общих операций. При работе на уровне ядра укажите макет ввода, точность накопления и конфигурацию выполнения.