RC RANDOM CHAOS

Rust for CUDA won't make your models faster

Nvidia's native Rust support for GPU programming is a reliability shift at the systems layer, not a speedup - it matters only if you write custom device code.

· 10 min read
Rust for CUDA won't make your models faster

Nvidia making Rust a first-class language for CUDA does not change what a GPU can do. It changes who can safely program it, and how much of your stack survives contact with production. The hardware still runs the same kernels and executes the same parallel math. What moves is the language boundary between your application code and the silicon - and that boundary is exactly where most AI infrastructure quietly rots. Memory errors, undefined behaviour, and integration bugs in custom GPU code have historically been C++ problems that only show up under load, at 3am, in a distributed job that already cost you a few hundred dollars to schedule.

For people building LLM systems and agent pipelines, the practical impact is narrow but real. You get fewer memory-safety bugs in hand-written kernels, a type system that catches interface mismatches before they reach a running cluster, and a build-and-dependency story through cargo that behaves like the rest of modern software instead of a decade-old Makefile you are afraid to touch. It does not make your model faster. It does not replace CUDA C++ overnight. And it does not matter at all if you only ever call PyTorch and never write a line of device code.

So the honest framing is this: native Rust support is an ergonomics and reliability shift at the systems layer, not a capability leap at the model layer. If you write or maintain custom GPU code - fused kernels, custom attention variants, inference runtimes, data loaders that touch the device directly - it lowers the cost of doing that work correctly. If you do not, treat it as a signal about where the tooling is heading over the next few years, not as something you act on this week. Knowing which of those two situations you are in is the entire decision.

Underneath the announcement, the mechanics are straightforward once you strip the framing away. CUDA has always been a C++ world. You wrote kernels in a C++ dialect, compiled them with nvcc down to PTX - Nvidia’s intermediate GPU assembly - and the driver turned that into machine code for whatever card you were running on. Rust support means the Rust compiler toolchain can now target that same path as a supported route, so you write device code in Rust, it lowers to PTX, and it runs on the same hardware through the same driver stack. Nothing about the execution model changes. What changes is the language you express it in, and the guarantees that language gives you on the way down.

Those guarantees are the actual point. Rust’s ownership and borrow-checking model catches a large class of memory bugs at compile time - use-after-free, data races on shared buffers, unchecked pointer arithmetic - the same bugs that are trivial to introduce in CUDA C++ and painful to diagnose once they are running across dozens of GPUs. In a single-threaded script this feels like bureaucracy. In a system where a kernel is writing into device memory that three other streams are reading from, it is the difference between a compiler error and a corrupted tensor you discover four layers downstream as a quietly wrong model output. The compiler moves the failure from runtime to build time, which is the only place failures are cheap.

The second shift is operational rather than linguistic. CUDA C++ has never had a package manager or dependency resolver that engineers actually enjoy using; builds are bespoke, reproducibility is fragile, and pulling in someone else’s kernel usually means vendoring source and praying. Rust brings cargo, a versioned dependency graph, and a build system that is boringly deterministic. For teams trying to treat GPU code as maintained infrastructure - reviewed, tested, versioned, shipped in CI like everything else - that is the part that compounds. It is not glamorous. It is the plumbing that decides whether custom GPU code becomes a shared, durable asset or a single engineer’s private liability.

The first mistake is reading this as a performance story. It is not. Rust generates PTX through the same compiler infrastructure and runs on the same hardware, so a correctly written kernel in Rust and the equivalent in C++ land in roughly the same place at runtime. Your model does not train faster because the kernel around it is memory-safe. If your inference is slow, the cause is almost certainly batching, memory bandwidth, kernel launch overhead, or a model that is simply large - none of which a language change touches. Expecting a speedup here is expecting the wrong thing from the right tool.

The second mistake is assuming you now have to rewrite something. The overwhelming majority of LLM and agent work never touches a kernel at all. You are calling into PyTorch, vLLM, TensorRT, or a hosted API, and every one of those already sits on mountains of battle-tested CUDA C++ that is not going anywhere. Rust support is additive, not a migration mandate. If your team has no custom device code today, the correct response to this announcement is to note it and move on. Rewriting working, framework-provided kernels into a newer toolchain to feel current is exactly the kind of complexity-for-its-own-sake that breaks production systems.

The third mistake is the reflexive one in this niche: treating a lower-level improvement as license to add more layers. A safer kernel language does not mean you should hand-roll your own attention implementation, and it certainly does not mean you should wrap that kernel in an agent that decides at runtime how to call it. Most teams do not have a kernel problem - they have an orchestration problem, and no language at the GPU layer fixes a pipeline that is fragile above it. The value of native Rust shows up only at the specific layer where you were already going to write device code and wanted it to be memory-safe, testable, and maintainable. Anywhere above that layer, this announcement changes nothing, and pretending otherwise just adds surface area you will have to defend later.

The useful move is not to adopt Rust. It is to draw a hard line around the part of your stack where owning device code is genuinely unavoidable, and apply Rust only inside that line. For most teams that region is small: one or two custom kernels, a dequantization path, a data loader that touches the device directly, a fused operation the framework does not ship. Everything outside that line stays exactly as it is - PyTorch, vLLM, TensorRT, the hosted API - because rewriting working framework code buys you nothing but risk. The skill here is scoping, not conversion. You are deciding where the boundary sits, then leaving both sides of it alone.

Inside that boundary, introduce Rust the way you would introduce any new component into a running system: additively, behind a stable interface, with the old path still available. A kernel written in Rust lowers to PTX and can be exposed through a C ABI or wrapped as a custom operator, which means the calling code in Python or C++ neither knows nor cares what language the kernel was written in. That interface is the contract. Write the new kernel in Rust, expose it through the same signature the rest of your stack already expects, and you can swap it in behind a flag, compare it against the existing implementation, and roll it back without touching anything upstream.

The part that actually pays off is the discipline around that kernel, not the kernel itself. Stand up a correctness harness that runs the Rust kernel and a reference implementation - PyTorch eager is usually fine - over a range of shapes, dtypes, and edge-case inputs, and asserts the outputs match within a numerical tolerance you have chosen deliberately. Run it in CI on every change. Pin your dependencies with a cargo lockfile so the build is reproducible on a fresh machine and not just on the laptop of whoever wrote it. Benchmark before and after to confirm you have not regressed latency, because memory safety is not a performance argument and you should verify you paid nothing for it. Do that, and the kernel stops being a fragile artifact one person understands and becomes a reviewed, tested, versioned component the team owns.

If none of that describes your work - no custom kernels, no device-level code, nothing below the framework - the practical step is to do nothing and record why. Note that the toolchain is maturing, that Rust is now a supported path to PTX, and that the calculus may change if you ever need a custom operation the framework does not provide. That is a real decision, not avoidance. Adopting a lower-level tool you have no use for is how teams manufacture complexity they later have to defend.

Take a team running self-hosted inference for a fine-tuned model on their own GPUs. To fit the model in memory and hit their latency target, they wrote a custom kernel that dequantizes weights and fuses a couple of operations the framework did not combine. It works. It also fails roughly once a week under peak concurrency: a handful of requests come back with garbled tokens, no error is thrown, and the logs show nothing because nothing crashed. After days of investigation someone finds it - a shared device buffer being written by one stream while another reads it, a race that only surfaces when enough requests overlap. In CUDA C++ that bug is invisible until it corrupts output under load.

Rewrite that one kernel in Rust and the specific class of bug changes category. The aliasing that caused the race - two references to the same buffer, one of them mutable, live at the same time - is the exact pattern the borrow checker rejects at compile time. The engineer does not find the race in production a week later; the code does not build until the sharing is made explicit and correct. The kernel goes behind the same C ABI the Python side already calls, so vLLM, the scheduler, the batching logic, and everything else in the stack stay untouched. The team adds a correctness harness comparing the new kernel against the framework’s slower reference path, wires it into CI, and pins the build with a lockfile. The custom kernel is now something a second engineer can read, test, and modify without inheriting a landmine.

What did not change is just as important. The model runs at the same speed, because the math and the hardware are identical; the memory-safe kernel is exactly as fast as the C++ one it replaced. The team’s real capacity limits - batch size, KV-cache pressure, memory bandwidth, kernel launch overhead - are untouched, and profiling is still the only way to move them. If their latency problem lived in scheduling or batching rather than in that one kernel, Rust would have contributed nothing to it. The win is narrow and precisely located: one recurring, expensive, hard-to-see failure mode moved from runtime to compile time, in the one place they were always going to own device code anyway.

Native Rust for GPU programming is a reliability and maintainability shift at the systems layer, and nothing more than that. If you write or maintain custom device code, it lowers the cost of doing that work correctly - fewer memory bugs, interface mismatches caught by the type system, a dependency and build story that behaves like the rest of your software. If you do not write device code, it is a signal about where the tooling is heading, worth registering and not worth acting on this week. The entire decision is knowing which of those two you are.

For teams, the quiet effect is on ownership. Custom GPU code has historically been a single engineer’s private territory - undocumented, untested, understood by one person, and a liability the moment they leave. A memory-safe language with a real build system and a dependency graph is what lets that code be reviewed, tested, and shipped through the same pipeline as everything else. That is the change worth caring about: not that GPU programming gets faster, but that it becomes something a team can own together instead of a risk concentrated in one head. Over a few years that is what moves custom kernels from private liability to durable infrastructure.

None of this fixes the problem most teams actually have. A safer kernel language does not repair a fragile pipeline, an over-agented workflow, or an orchestration layer held together by retries and hope - those failures live well above the GPU, and no improvement at the silicon boundary reaches them. The temptation this announcement creates is to treat a lower-level tool as a reason to build lower-level things you do not need. Resist it. Adopt Rust exactly where you already write device code and want it to be correct, and nowhere else. The best outcome here is boring: a small, dangerous corner of your stack becomes safe and maintainable, and everything above it stays as simple as it was. If you find yourself reaching for it anywhere else, the problem was never the language.

Share

Keep Reading

Latest on the Wire

Full wire →

New signal daily · RSS

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.