- Se presentó un proyecto de demostración donde una sola base de código escrita en Rust funciona en CUDA, Vulkan (SPIR-V), Metal, DirectX 12, WebGPU y CPU, es decir, en todas las principales plataformas de GPU y CPU
- La programación tradicional de GPU genera complejidad y duplicación al requerir lenguajes separados como GLSL, HLSL, etc., pero esta demo soporta todos los destinos de GPU usando solo código Rust puro
- Integra proyectos clave como Rust GPU (SPIR-V), Rust CUDA (NVVM IR) y Naga (capa de traducción entre lenguajes de GPU); el mismo algoritmo de ordenamiento bitónico funciona en CPU y en todas las GPU, y las funciones del lenguaje Rust como no_std, compilación condicional, newtype, enum y trait también se aplican tal cual al código GPU
- Aunque aún quedan tareas por mejorar, como la integración oficial en
rustc, la depuración y la consistencia de las API, se trata de un intento que marca un punto de inflexión para el cómputo multiplataforma en GPU
Hacer realidad el cómputo multiplataforma para GPU con Rust
Introducción al proyecto y su importancia
- Una sola base de código en Rust permite ejecutar el mismo código de kernel en CUDA (NVIDIA), Vulkan (SPIR-V), Metal (Apple), DirectX 12 (Windows), WebGPU (navegador) y CPU
- Sin usar lenguajes dedicados para shaders o kernels como GLSL o HLSL, es posible procesar la misma operación en GPU y CPU solo con código Rust puro
- Esto reduce drásticamente la duplicación de código y la complejidad entre GPU y CPU, y además lleva a la programación de GPU las ventajas del ecosistema Rust y de su lenguaje, como la seguridad de tipos, las pruebas, la documentación y la gestión de builds
Contexto
- La programación tradicional de GPU exige usar lenguajes de shaders especializados para cada plataforma, como GLSL, HLSL, MSL o WGSL
- Como resultado, el código de CPU y GPU queda separado, lo que aumenta la duplicación de lógica y la complejidad de desarrollo
- Para resolverlo, la comunidad de Rust viene impulsando un enfoque que compila Rust común y corriente para ejecutarlo en GPU
- Rust GPU: compila código Rust a SPIR-V, y funciona en Vulkan y en GPU compatibles con SPIR-V
- Rust CUDA: compila código Rust al IR de NVIDIA CUDA (NVVM IR, PTX) para ejecutarlo en CUDA
- Naga: una capa intermedia que soporta conversiones entre varios lenguajes de GPU (WGSL, SPIR-V, GLSL, MSL, HLSL), usada principalmente en el proyecto
wgpu. Se encarga de la portabilidad del backend
- Aunque cada proyecto comenzó de forma independiente y con API y estructuras distintas, la colaboración reciente permitió ejecutar GPU con una base de código compartida
Cómo está implementado
- En la demo de ejemplo, el algoritmo de ordenamiento bitónico se implementa con un único código Rust, y ese mismo código corre sobre todos los destinos, tanto GPU como CPU
- Términos principales de la implementación
- Host: código Rust que se ejecuta en la CPU e inicia el trabajo
- Device: GPU o CPU donde realmente se ejecuta el kernel
- Driver API: API de bajo nivel para comunicarse con el dispositivo, como CUDA, Vulkan, Metal o DX12
- Backend: abstracción en Rust sobre la Driver API objetivo (
cust, ash, wgpu, etc.)
- Mediante combinaciones de targets y feature flags de Rust se puede controlar la selección del backend y el proceso de build
- Por ejemplo,
wgpu, ash, cuda y otros backends ofrecen opciones de build independientes
- También es posible construir varios backends dentro de un solo binario y seleccionarlos dinámicamente en tiempo de ejecución
Flujo de compilación del kernel
- Según el target y los features, durante el build se generan binarios de ejecución para GPU como SPIR-V o PTX, que luego se embeben en archivos objeto
- En tiempo de ejecución se carga el kernel embebido y, con ayuda de Naga, se convierte al formato requerido por cada plataforma antes de ejecutarlo
- Ejemplos:
- macOS: conversión de SPIR-V a MSL → ejecución en Metal
- Windows: conversión de SPIR-V a HLSL → ejecución en DX12
- Linux/Android: SPIR-V → ejecución en Vulkan
- CUDA: PTX se compila a SASS, se sube a la GPU y se ejecuta
Técnicas de programación GPU amigables con Rust
-
Soporte para no_std
- Como las GPU no tienen soporte de sistema operativo, el uso de
no_std en Rust es indispensable
- Dado que el ecosistema base de Rust ya contempla entornos sin sistema operativo, como embebidos, firmware o kernels, las GPU también pueden soportarse con el enfoque estándar de Rust sin necesitar un “modo especial” aparte
-
Compilación condicional
- Con atributos
cfg y combinaciones de feature flags se puede escribir de forma clara y concisa el código separado por plataforma y por GPU/CPU
- El IDE y el compilador entienden todas las rutas de código, lo que mejora la confiabilidad y la productividad
-
Uso de newtype
- Para evitar errores a nivel de tipos en índices implícitos o mapeos de structs, se usa
newtype
- Con el atributo
#[repr(transparent)] se obtiene abstracción de tipos prácticamente sin overhead real
-
Enum y representaciones seguras
- En lugar de usar números mágicos, se usan
enum de Rust con #[repr(u32)] para garantizar el mapeo a enteros nativos
- El pattern matching y la exhaustividad permiten construir código más seguro
- Eso sí, al intercambiar valores de
enum en buffers compartidos entre CPU y GPU, hay que asegurarse de que todos los valores correspondan a enums válidos
-
Algoritmos genéricos basados en trait
- Los
trait permiten abstraer comparaciones, conversiones, operaciones y otras tareas comunes de GPU para distintos tipos de valores
- Al definir claramente los
trait bounds de un algoritmo genérico, se logra combinar seguridad de tipos y rendimiento
-
#[inline] y optimización de rendimiento
- El uso de
#[inline] ayuda a que las capas de abstracción desaparezcan del resultado final compilado
- Está diseñado para que la abstracción no tenga costo, algo importante en GPU por temas de rendimiento y limitaciones de stack
-
Composición de structs y agrupación semántica
- Los parámetros complejos de GPU se agrupan por significado dentro de
struct, lo que mejora la seguridad de tipos y evita la explosión del número de parámetros
- Con el patrón de smart constructor se bloquean estados inválidos desde la etapa de compilación
-
Layout de memoria y control con #[repr(C)]
- Para la compatibilidad de datos con la GPU, el layout de los
struct se define explícitamente con #[repr(C)]
- También se menciona la necesidad futura de más soporte del lenguaje, como la automatización del padding según cada GPU
-
Pattern matching
- Como una versión ampliada de
switch/case, permite manejar de forma clara todos los estados y bifurcaciones dentro del código GPU
- Además, el compilador puede verificar rutas de código y optimizar rendimiento
-
Funciones genéricas
- Permiten implementar la misma lógica para múltiples tipos de datos sin depender de uno solo
- Los
trait bounds y la monomorfización mejoran el mantenimiento y facilitan las pruebas
-
Macros derive
- Automatizan la implementación de traits aptos para GPU como
Copy, Clone, Debug, PartialEq y Pod
- Esto reduce boilerplate y mejora la seguridad
-
Sistema de módulos y gestión de workspaces
- El sistema de paquetes y módulos de Rust permite estructurar el código del host, los kernels, los tipos y las fuentes por backend
- Con
Cargo workspace y dependencias de workspace se mantiene la consistencia entre crates y se facilita el mantenimiento
-
Formateo, lint y documentación
- El código GPU puede gestionarse exactamente igual que el resto del código Rust usando herramientas estándar como
rustfmt y clippy, manteniendo una calidad consistente
- También se puede documentar el código GPU con doc comments y
cargo doc
-
Scripts de build
- Mediante
build.rs de Cargo se automatiza el build del kernel y el embebido de binarios en función de los feature flags
-
Unit tests y productividad de desarrollo
- Como el código de kernels GPU también puede probarse en CPU, es más fácil desarrollar y detectar bugs
- Se pueden usar herramientas tradicionales como depuración con
println y gdb/lldb
- Incluso los kernels de Vulkan pueden probarse en CI con drivers por software como SwiftShader o
lavapipe
- También hay buena integración con herramientas de terceros para pruebas, medición de cobertura y property-based testing
Tareas pendientes y desafíos en la experiencia de desarrollo
- Como los backends de GPU no están integrados en el compilador oficial de Rust, todavía hace falta usar backends de codegen separados (
spirv, nvvm) y fijar versiones nightly
- El target de CUDA depende de LLVM 7.1 de NVIDIA, por lo que en distribuciones modernas de Linux se requiere un build aparte
- Aún falta madurez en la experiencia de build y depuración de kernels, y persisten problemas con el tracing de errores y la información de debug
- Las API y hasta los nombres de bibliotecas estándar difieren entre Rust GPU y Rust CUDA, lo que genera confusión
- A largo plazo, hace falta reforzar la consistencia y la integración orientada a GPU dentro del lenguaje Rust y de su ecosistema en general
Participación de la comunidad y futuro
- Ya se logró ejecutar el mismo código Rust en todas las principales plataformas de GPU
- De ahora en adelante, los retos serán mejorar el build y la depuración, ampliar el soporte del lenguaje Rust y de las API, y afinar el rendimiento
- Los desarrolladores que quieran participar o contribuir pueden consultar los repositorios de GitHub de
rust-gpu y rust-cuda
1 comentarios
Comentarios de Hacker News
Es realmente impresionante que esta tecnología sea posible.
Pero mi caso de uso consiste en ejecutarlo sobre hardware arbitrario del cliente, así que prefiero no confiar en todas las capas de abstracción construidas encima de las API de GPU.
El objetivo es aprovechar al máximo los detalles de bajo nivel de la GPU, y los enfoques que consideran molestos esos detalles terminan causando bugs y pérdida de rendimiento.
Eso es porque cada objetivo es significativamente distinto.
Para superar esto, creo que un sistema similar tendría que ser ofrecido directamente por los propios proveedores.
Pero como todavía no hay acuerdo entre fabricantes, parece que las diferencias entre plataformas siguen siendo grandes.
En algunos casos hay excepciones como Angle, pero incluso ahí la estabilidad solo se consigue limitando funcionalidades, y eso al final implica pérdida de rendimiento.
Aun así, claramente ayuda que se puedan usar enfoques como la compilación condicional.
Rust es un lenguaje de sistemas, así que puedes tener todo el control que quieras.
Planeamos reflejar los detalles y las API de la GPU en el lenguaje y en las bibliotecas core/std, y exponer las capacidades de la GPU y del driver mediante el sistema
cfg().(soy el autor)
Pienso exactamente igual.
Siempre soy cauteloso al construir algo comercial sobre capas de abstracción, adaptadores o capas de traducción que no sé si recibirán soporte suficiente en el futuro.
Ya casi estamos en 2025 y todavía siento que hace falta urgentemente un estándar abierto que todos los proveedores soporten y que permita usar todas las capacidades del hardware GPU moderno.
Dado el estado actual de las cosas, también resulta significativo que Nvidia, que creó la barrera de entrada de software más fuerte, esté al frente de Khronos.
Parece que te importa mucho el rendimiento, así que quiero preguntarte por curiosidad.
A mí me parece que el estado actual del mundo GPU se parece mucho a cómo eran antes las CPU.
En CPU se usaba una estructura de compilador en tres niveles: se optimizaba en una capa intermedia y luego en la capa final se generaba código para cada hardware.
La ventaja de esa estructura es que la capa de abstracción dura mucho tiempo y el compilador se vuelve más inteligente con el tiempo.
Me pregunto si algo así también es posible en GPU.
O si la diversidad de GPU es tan grande que eso es imposible o antieconómico, o si claramente ese es el rumbo pero la tecnología aún no llega ahí.
Es totalmente cierto.
No me queda muy claro en qué sería mejor ejecutar Rust en una GPU de Nvidia que usar código CUDA existente.
Entiendo la idea de agregar abstracción, pero al final da una sensación de “hacer de todo un poco”.
En realidad, todo es abstracción.
CUDA también es, en esencia, solo una abstracción que unifica conceptualmente hardware que en realidad es completamente distinto.
Desarrollo apps nativas de audio, y aquí cada ciclo de cómputo importa.
Además, no necesito shaders gráficos sino una API completa de cómputo.
Me pregunto si un pipeline como "Rust -> WebGPU -> SPIR-V -> MSL -> Metal" es sólido en términos de rendimiento.
Se ven demasiadas etapas de traducción, así que parece frágil e impredecible.
Lo mismo con "... -> Vulkan -> MoltenVk -> ...".
En cambio, "Julia -> Metal" se salta MSL y puede aprovechar directamente optimizaciones especiales de Apple Silicon, como Unified Memory.
La innovación aquí no es el lenguaje de shaders, sino el hecho de usar un lenguaje de programación completo como Rust.
Rust soporta muchas funciones como newtypes, traits, macros, etc.
Si usas rust-gpu, no necesariamente tienes que pasar por la capa de WebGPU.
rust-gpu es el backend de generación de código del compilador.
La estructura permite compilar Rust MIR directamente a SPIR-V.
¿Es sólido en rendimiento el pipeline "Rust -> WebGPU -> SPIR-V -> MSL -> Metal"?
Básicamente es un concepto similar al enfoque de optimización que usa Apple con Clang para GPU.
SPIR-V es una representación intermedia, como el IR que usa LLVM, así que se puede optimizar para cada sistema.
En teoría, con una sola base de código se puede apuntar a todas las GPU raster compatibles.
En cambio, el stack Julia -> Metal es relativamente menos portable.
Para desarrolladores enfocados en una sola plataforma, como en plugins de audio, eso no importa, pero para empresas multiplataforma como u-he o Spectrasonics, un pipeline más complejo basado en SPIR-V podría resultar más atractivo.
Para cómputo numérico y sus optimizaciones posteriores, Julia encaja mucho mejor que Rust, que es un lenguaje de sistemas.
Si miras la matriz de compatibilidad de Rust-CUDA, se nota que la demanda de Rust para programar en CUDA es muy baja.
Faltan la mayoría de las funciones de CUDA que a la gente le gustan, y si realmente hubiera mucha demanda, ya habría más avances, pero no es el caso.
Parece que los programadores de CUDA no tienen mucho interés en usar Rust.
https://github.com/Rust-GPU/Rust-CUDA/blob/main/guide/src/features.md
Aunque tengo código que me gustaría correr en GPU, todo lo relacionado con programación GPU es tan doloroso que al final no lo hago.
Me da la impresión de que el verdadero uso de rust-gpu podría ser convertir a desarrolladores de CPU en desarrolladores de GPU, aunque sea aceptando cierta pérdida de rendimiento.
Si ya conoces bien el mundo GPU y dominas cuda/vulkan/metal/dx, probablemente no te resulte tan atractiva una herramienta así.
Soy solo un desarrollador web, así que tal vez sea una pregunta tonta, pero nunca he hecho programación GPU.
Me pregunto si WebGPU no resuelve ya todos estos problemas, dado que es una API única compatible con todos los backends de GPU.
Parece que WebGPU es también uno de los backends soportados; si es así, ¿no sería simplemente otra abstracción encima de abstracciones existentes que al final terminan llamando al backend GPU nativo?
No exactamente.
WebGPU es una API para controlar la GPU desde la CPU y hacer que ejecute shaders y otras tareas gráficas, como D3D, Vulkan o SDL GPU.
Rust-GPU es un lenguaje para escribir el código de shader que realmente se ejecuta en la GPU, similar a HLSL, GLSL o WGSL.
Cuando Microsoft tenía influencia, estaba DirectX.
Pero hoy no sé bien hasta qué punto los fabricantes de GPU están implementando API dedicadas para sus propias tecnologías propietarias.
Hay funciones peculiares como DLSS, MFG, RTX y otras.
Si fueras el villano de una caricatura, podrías hacer las API existentes intencionalmente lentas y ofrecer rendimiento rápido solo en una API nueva y exclusiva del proveedor.
Yo también soy desarrollador web, así que no lo sé bien, pero al menos esto es el tipo de cosa con la que un LLM termina entrenándose.
Yo diría que WebGPU se parece más a una API de mínimo común denominador.
Por ejemplo, el editor Zed apunta directamente a Metal en Mac.
Y además, cada quien tiene una idea distinta de lo que significa “común”.
Igual que con OpenGL frente a Vulkan, las empresas con poder intentan convertir sus propios ecosistemas —CUDA, Metal, DirectX, etc.— en el estándar de facto del mercado.
Si de verdad fuera tan fácil, CUDA no estaría funcionando hoy como el enorme foso competitivo de Nvidia.
La base importante de este proyecto es el trabajo hecho en la implementación WebGPU de wgpu-rs.
WebGPU no es lo ideal para apps nativas.
Fue diseñado tomando como base versiones antiguas de Vulkan, especialmente pre-RTX, así que las API nativas más recientes han evolucionado bastante más desde entonces.
Todavía está en una etapa tosca, pero me cuesta creer que algo así sea posible.
Si este tipo de avances continúa, siento que existe el potencial de romper el fuerte problema de dependencia de proveedor en el software GPU y permitir competencia real entre fabricantes de hardware.
Incluso imagino un futuro en el que podamos escribir modelos de machine learning en Rust y ejecutarlos tanto en Nvidia como en AMD.
Claro, para obtener el máximo rendimiento habría que escribir código específico para cada proveedor, pero eso ya sería un tema de optimización.
Aun así, podrías tener kernels portables que corran en múltiples plataformas.
Existe un framework de machine learning en Rust llamado https://burn.dev, con varios backends como CUDA y ROCm.
Vale la pena echarle un vistazo.
Un futuro donde escribas modelos de machine learning en Rust y los ejecutes tanto en Nvidia como en AMD me parece difícil dentro de los próximos diez años.
En la práctica, ecosistemas completos como JAX y Torch están basados en Python.
Lograr que todos los desarrolladores profesionales migren a herramientas Rust se siente casi imposible de imaginar.
Si cuentas las capas de abstracción:
cust,ash,wgpuwgpuy similaresHay al menos seis niveles de complejidad ocultos.
Me cuesta creer que sea posible atravesar todas esas capas y aun así reflejar en rendimiento las particularidades de cada plataforma.
Lo que hace rust-gpu al final es compilar a SPIR-V (es decir, al IR de Vulkan).
Así que las capas 2 y 3 pueden omitirse o mantenerse en paralelo.
También puedes seguir aprovechando para el desarrollo de shaders GPU herramientas del ecosistema Rust como
cargo,cargo test,cargo clippyyrust-analyzer.En realidad no creo que la programación GPU sea difícil porque la arquitectura GPU sea demasiado alienígena, sino porque todo el ecosistema está atado a proveedores y frenado por herramientas antiguas.
El demo ciertamente se parece más a una máquina de Rube Goldberg compleja, pero eso es porque recién ahora algo así es posible por primera vez.
Con el tiempo se volverá más natural e integrado.
Y otra ventaja del ecosistema Rust es que puedes desarrollar con tanta abstracción o tanto detalle como quieras.
Por ejemplo, puedes usar funciones específicas de plataforma con
std::arch, o incluso escribir ensamblador.También puedes reemplazar el asignador o el manejador de pánicos, y cuando se active la futura funcionalidad de externally implemented items, habrá todavía más flexibilidad para manejar las capas de abstracción al nivel que quieras.
Es una buena observación.
Pero las capas 4 a 6 siempre existen también en shaders o en código CUDA.
Y las capas 1 y 3 en realidad solo se reemplazan por otras capas distintas, especialmente si quieres multiplataforma.
Incluso si este proyecto de Rust agrega una capa de abstracción, sería más o menos solo una.
Y como alguien que trabaja justamente en las capas 4 a 6, puedo asegurar que ahí también hay una enorme cantidad de complejidad oculta.
Para ser honestos, a veces incluso hay más capas :P
Viéndolo de forma realista, la mayoría de los usuarios solo terminará lidiando hasta la capa (3) o la (4).
En la práctica no se agrega una cantidad tan grande de pasos extra.
Y por cierto, por encima de la capa 6 también hay más abstracciones.
El firmware y la microarquitectura implementan el conjunto de instrucciones que creemos tener.
No me parece tan diferente de mantener compiladores y runtimes distintos para diferentes arquitecturas de CPU.
También hay distintas convenciones de llamada, diferencias de endianness, etc., y a nivel de hardware también existen firmware y microcódigo.
Me sorprende muchísimo que crates existentes de
no_std+no allocpuedan correr en GPU casi sin modificaciones.Realmente da la sensación de abrir muchas ideas nuevas de aplicación.
Es realmente impresionante.
La lista de proyectos Rust GPU ya se volvió enorme.
Este parece estar más cerca de una abstracción de bajo nivel que burn, y burn a su vez está más abajo que candle.
Ahora parecería que lo que falta es agregar un backend como naga a los proyectos de más arriba.
Da la impresión de que todos están construyendo algo sobre bases distintas, aunque eso probablemente se deba a que el trabajo de naga es más reciente.
También quisiera añadir que burn está enfocado en soporte de plataformas.
Pero viendo bien, el único backend que usa naga es wgpu.
Entonces, al final, ¿no bastaría con usar solo wgpu?
En resumen, sería wgpu/ash(vulkan, metal) o cuda.
Dato extra: otro crate cercano a este esfuerzo
https://github.com/tracel-ai/cubecl
[0]: https://github.com/tracel-ai/burn
[1]: https://github.com/huggingface/candle/
Ahí también está resumido lo relacionado con CubeCL.
Me pregunto si de verdad esto significa que “Rust” corre sobre la GPU.
Viendo por encima el código, parece una estructura donde se usa sintaxis de Rust con un montón de macros de programación y al final se monta encima un lenguaje de shaders.
La programación GPU es tan distinta que creo que requiere atención especial.
Meter esta abstracción podría volver imposibles ciertas optimizaciones específicas.
Me alegra mucho ver este proyecto.
Siento que está ayudando bastante a desarrolladores que no quieren verse atrapados en guerras de plataforma.
También me parecen buenos ejemplos como https://github.com/cogentcore/webgpu.
Yo uso golang y solo me importa poder aprovechar bien la GPU en todas las plataformas, y gracias a esto se puede usar la GPU en cualquier lado.
Muchas gracias a Rust.