Главная/Блог/Гайд/CUDA Rust: Два пути для GPU-ядер…
Гайд4 мин чтения · 8 сентября 2026 г.

CUDA Rust: Два пути для GPU-ядер (cuda-oxide vs cutile-rs)

NVIDIA представила нативную поддержку Rust для GPU. Разбираем два подхода: SIMT через cuda-oxide и Tile через cutile-rs — от установки до примеров кода.

CUDA Rust: Два пути для GPU-ядер (cuda-oxide vs cutile-rs)

В сентябре 2026 года NVIDIA официально анонсировала развитие экосистемы CUDA Rust. Если раньше Rust использовался преимущественно для системной части (драйверы, инфраструктура), то теперь разработчики могут писать сами GPU-ядра на Rust, компилируя их напрямую в PTX. Это закрывает разрыв между безопасностью памяти на этапе компиляции и производительностью GPU.

Существует два основных пути разработки: SIMT (Single Instruction, Multiple Threads) — классический подход, знакомый по CUDA C++, и Tile — более высокоуровневая абстракция, где компилятор сам управляет маппингом потоков. Оба проекта обеспечивают безопасность памяти, но требуют разных инструментов и подходов.

01cuda-oxide: Нативная компиляция SIMT-ядер

cda-oxide — это кастомный бэкенд кодогенерации для rustc. Он перехватывает компиляцию, маршрутизирует функции с атрибутом #[kernel] через IR-фреймворк Pliron и LLVM, превращая их в PTX. Этот проект находится на ранней стадии альфа-тестирования и требует использования nightly-версии Rust.

Для работы потребуется Linux, GPU с вычислительной способностью 8.0+, CUDA Toolkit 12.x+ и clang. Установка осуществляется через Cargo:

terminalbash
cargo +nightly-2026-04-03 install --git https://github.com/NVlabs/cuda-oxide.git cargo-oxide

После установки создайте проект и проверьте окружение:

terminalbash
cargo oxide new vecadd_demo
cd vecadd_demo
cargo oxide doctor
cargo oxide run

Первый запуск cargo oxide run будет долгим, так как компилируется сам бэкенд. Далее используется кэш. Пример ядра сложения векторов выглядит так:

terminalrust
use cuda_device::{kernel, launch_bounds, launch_contract, thread, DisjointSlice};
use cuda_host::cuda_module;
use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig1D};

#[cuda_module]
mod kernels {
    use super::*;

    #[kernel]
    #[launch_bounds(256)]
    #[launch_contract(domain = 1, block = (256, 1, 1))]
    pub fn vecadd(a: &[f32], b: &[f32], mut c: DisjointSlice<f32>) {
        let idx = thread::index_1d();
        let idx_raw = idx.get();
        if let Some(c_elem) = c.get_mut(idx) {
            *c_elem = a[idx_raw] + b[idx_raw];
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let ctx = CudaContext::new(0)?;
    let stream = ctx.default_stream();
    const N: usize = 1024;
    
    // Инициализация хост-данных
    let a_host: Vec<f32> = (0..N).map(|i| i as f32).collect();
    let b_host: Vec<f32> = (0..N).map(|i| (i * 2) as f32).collect();
    
    // Перенос на устройство
    let a_dev = DeviceBuffer::from_host(&stream, &a_host)?;
    let b_dev = DeviceBuffer::from_host(&stream, &b_host)?;
    let mut c_dev = DeviceBuffer::zeroed(&stream, N)?;

    // Запуск ядра
    let module = unsafe { kernels::load(&ctx)? };
    let prepared = module.prepare_vecadd(LaunchConfig1D::new((N as u32).div_ceil(256), 256, 0))?;
    module.vecadd(&stream, &prepared, &a_dev, &b_dev, &mut c_dev)?;

    // Проверка результата
    let c_host = c_dev.to_host_vec(&stream)?;
    let errors = (0..N).filter(|&i| (c_host[i] - (a_host[i] + b_host[i])).abs() > 1e-5).count();
    
    if errors == 0 {
        println!("PASSED: all {} elements correct", N);
    } else {
        eprintln!("FAILED: {} errors", errors);
        std::process::exit(1);
    }
    Ok(())
}
💡
Безопасность памяти. Ключевая фишка cuda-oxide — тип DisjointSlice. Он гарантирует, что каждый поток получает эксклюзивный доступ к своему элементу, исключая гонки данных на уровне компиляции, чего нельзя сделать с обычным &mut [f32].

02cutile-rs: Высокоуровневое программирование через Tile

cutile-rs работает на более высоком уровне абстракции. Вместо управления отдельными потоками вы оперируете «тайлами» (блоками данных). Компилятор сам решает, как маппировать эти тайлы на архитектуру GPU. Этот проект работает на стабильном Rust 1.89+ и не требует кастомного LLVM.

Проект уже интегрирован в HuggingFace Grout и mistral.rs. Установка и запуск примера:

terminalbash
git clone https://github.com/NVlabs/cutile-rs.git
cd cutile-rs
cargo run --example hello_world

В отличие от cuda-oxide, здесь нет необходимости вручную указывать launch_bounds или управлять индексами потоков. Вы описываете логику обработки одного тайла, а cutile-rs генерирует оптимальный код для конкретного GPU.

03Сравнение подходов

Выбор между SIMT и Tile зависит от задачи. Tile рекомендуется как стартовая точка, так как код становится более переносимым между архитектурами. SIMT нужен, когда требуется тонкая настройка памяти и потоков.

Характеристика cda-oxide (SIMT) cutile-rs (Tile)
Версия Rust Nightly (pinned) Stable 1.89+
Зависимость от LLVM Требует кастомный бэкенд Стандартный LLVM
Управление памятью DisjointSlice, ручные контракты Разделение тензоров, ownership
Статус Ранняя альфа Используется в продакшене (Grout)
Сложность Высокая (низкоуровневый контроль) Средняя (абстракция тайлов)
⚠️
Совместимость. Для cuda-oxide требуется GPU с Compute Capability 8.0 или выше (Ampere и новее). Старые карты (Turing и ниже) не поддерживаются данным методом компиляции.

04Кому подойдёт / что запустится

cuda-oxide подойдет системным программистам и разработчикам библиотек, которым нужен полный контроль над PTX-генерацией и возможность писать ядра в стиле CUDA C++, но с гарантиями Rust. Это выбор для тех, кто готов работать с nightly-сборками и отлаживать сложные контракты запуска.

cutile-rs идеален для разработчиков AI-инференса и высокоуровневых библиотек. Если вы пишете модели, где операции можно разбить на матричные или тензорные блоки, этот инструмент позволит ускорить разработку, избегая ручного управления потоками. Он уже работает в стеках HuggingFace и готов к интеграции в production-среды на стабильном Rust.

Оба проекта заявляют о планах по межъязыковой интероперабельности с CUDA C++ и Python, что позволит постепенно мигрировать существующие GPU-вычисления на Rust без полной переписи кодовой базы.

Источник: NVIDIA Developer ↗