ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
Topics
sre111localhost lab102performance70python58javascript47bundle size36benchmarking35node.js35web development21typescript19benchmark16content creation15All topics
About
Loading article…

ShopperCove

Hands-on notes on developer tools, written after the tool has actually been run.

All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove

On this page

  1. What this is
  2. Install
  3. Hello world
  4. The one concept you must understand first
  5. Next steps
  6. FAQ
  7. What is cuda-oxide?
  8. How do I install cutile-rs?
  9. Should I use the Tile or SIMT track?
  1. Blog

What Is CUDA Rust? cuda-oxide vs cutile-rs for GPU Kernels

NVIDIA now lets you write CUDA kernels natively in Rust, compiled to PTX, through two separate projects: cuda-oxide for SIMT and cutile-rs for the tile model.

10 September 2026·4 min read

Summary

Disclosure

This post contains affiliate links. If you buy through them, I may earn a commission at no extra cost to you. I only make firsthand claims about products I have actually used.

On this page
  1. What this is
  2. Install
  3. Hello world
  4. The one concept you must understand first
  5. Next steps
  6. FAQ
  7. What is cuda-oxide?
  8. How do I install cutile-rs?
  9. Should I use the Tile or SIMT track?

What this is

CUDA Rust is NVIDIA's September 2026 effort to let GPU kernels be written natively in Rust and compiled to PTX, through two projects: cuda-oxide for the SIMT model, where you write what one thread does, and cutile-rs for the Tile model, where you write what one tile of data does and the compiler maps it to the hardware. NVIDIA's guidance is to start with cutile-rs, a published crate that runs on stable Rust, and drop to cuda-oxide, which needs a pinned nightly toolchain, only when you need direct control over memory and threads.

Install

cutile-rs is the lighter of the two to get running: it's a published crate, needs no nightly toolchain, and needs no LLVM of your own — just a supported GPU, a compatible CUDA toolkit, a recent stable Rust toolchain, and Linux.

cargo new vecadd_demo
cd vecadd_demo
cargo add cutile

The SIMT track (cuda-oxide) is installed differently, as a custom rustc codegen backend driven by a Cargo subcommand, and needs a pinned nightly toolchain, clang with libclang headers, and a CUDA toolkit:

cargo +nightly install --git https://github.com/NVlabs/cuda-oxide.git cargo-oxide

Hello world

Both tracks ship the same example — elementwise addition over 1,024 floats — so you can compare them directly. Here is the Tile version. Paste it into src/main.rs and run cargo run:

use cutile::prelude::*;

// The macro captures this module's AST into the host binary. The kernel is
// JIT-compiled through CUDA Tile IR the first time it is actually launched.
#[cutile::module]
mod kernel {
    use cutile::core::*;

    #[cutile::entry()]
    fn add<const B: i32>(
        // B is the tile width, a static dimension. A different B produces a
        // different specialization.
        z: &mut Tensor<f32, { [B] }>, // exclusive output, one sub-tensor of B elements
        x: &Tensor<f32, { [-1] }>,    // shared input; -1 is a dynamic dimension, resolved at launch
        y: &Tensor<f32, { [-1] }>,
    ) {
        // This body runs once per mut sub-tensor, as a single logical thread.
        // Tile kernels load tiles, not scalars, from x and y.
        let tx = load_tile_like(x, z); // the slice of x lining up with this sub-tensor of z
        let ty = load_tile_like(y, z);
        z.store(tx + ty); // elementwise across the whole tile
    }
}

fn main() -> Result<(), Error> {
    let device = Device::new(0)?;
    let stream = device.new_stream()?;

    // These are lazy. Nothing has touched the GPU yet.
    let x = api::ones::<f32>(&[1024]);
    let y = api::ones::<f32>(&[1024]);

    // Partitioning does three things at once: gives each tile exclusive
    // ownership of its own 128-element chunk, fixes the grid at 1024/128 = 8
    // tiles, and supplies B.
    let z = api::zeros::<f32>(&[1024]).partition([128]);

    let c: Vec<f32> = kernel::add(z, x, y) // takes ownership of all three tensors
        .first()                           // ...and returns them; pick the output back out
        .unpartition()                     // drop the host-side partition wrapper; no data moves
        .to_host_vec()                     // record the copy back
        .sync_on(&stream)?;                // and only now does any of it run

    let errors = c.iter().filter(|&&v| (v - 2.0).abs() > 1e-5).count();
    if errors == 0 {
        println!("PASSED: all {} elements correct", c.len());
    } else {
        eprintln!("FAILED: {errors} errors");
    }
    Ok(())
}

The equivalent SIMT program (cuda-oxide) is built the same way, via cargo oxide new vecadd_demo && cargo oxide run, but needs a DisjointSlice<f32> type on the output parameter to make the same exclusivity guarantee that Tile gets from .partition() — full listing at the NVIDIA blog post.

The one concept you must understand first

Everything about both tracks follows from what unit of work the kernel body describes. In SIMT, the function body is what one thread does, and you're responsible for indexing into flat memory correctly for that thread — which is why cuda-oxide needs a type like DisjointSlice to prove, at compile time, that no two threads can write the same element. In Tile, the function body is what one tile does — a whole sub-tensor treated as a single logical unit — and the compiler, not you, decides how many real GPU threads back that tile and how the mapping changes across architectures. That's the whole reason NVIDIA's guidance is to reach for Tile first: your source code doesn't encode architecture-specific choices, so it doesn't need updating when the target GPU does. You only drop to SIMT when you need that lower-level control over memory and threads yourself (source).

Next steps

  • cutile-rs on GitHub — the crate itself, for the Tile track's API surface beyond this one kernel.
  • Tile IR compiler documentation — the compiler that both cutile-rs and the C++/Python tile frontends target; useful once you want to understand what's happening between your #[cutile::entry] function and PTX.
  • cuda-oxide on GitHub — the SIMT track's repo, for when a kernel needs direct control over memory and threads that the Tile model abstracts away.

The sources do not give version numbers, pricing, or benchmark figures for either track, and do not describe the promised inter-language interop beyond stating that it's planned.

FAQ

What is cuda-oxide?

The SIMT track of CUDA Rust: a custom rustc codegen backend driven by cargo oxide. It needs a pinned nightly toolchain, clang with libclang headers and a CUDA toolkit, and uses types like DisjointSlice to prove no two threads write the same element.

How do I install cutile-rs?

Run cargo add cutile in a new project. It needs a supported GPU, a compatible CUDA toolkit, a recent stable Rust toolchain and Linux, with no nightly and no separate LLVM.

Should I use the Tile or SIMT track?

Tile first, per NVIDIA. Tile source doesn't encode architecture-specific choices, so it needn't change when the target GPU does. Use SIMT when you need low-level control over memory and threads.

rustcudagpukernelssimttile irprogramming

Sources

  1. 01
    Introducing CUDA Rust: Two Tracks for Writing GPU Kernels

    In September 2026, NVIDIA announced it is leaning into native GPU programming in Rust. CUDA C++ and CUDA Python are mature, enterprise-grade toolchains, and NVIDIA will be growing and maturing CUDA R…

Explore the product

Untested by us. Check what's included, the license and refund terms before you buy.

Ready Client Sites for prebuilt websites

See Ready Client Sites details

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Explore the product

Untested by us. Check what's included, the license and refund terms before you buy.

Ready Client Sites for prebuilt websites

See Ready Client Sites details

Keep reading

  • What Does yield Do in Python? Generators Explained

    Generators, lazily: what yield actually does to a function.

    Summary12 Oct 20261 min read

  • What Is the Walrus Operator (:=) in Python? Examples

    Assignment expressions, explained with the examples that actually help.

    Summary12 Oct 20261 min read

  • How Do I Get the Current UTC Time in Python Today?

    Use a timezone-aware datetime; utcnow() is deprecated since Python 3.12.

    Summary12 Oct 20261 min read