10 pontos por GN⁺ 2025-07-27 | 1 comentários | Compartilhar no WhatsApp
  • Foi apresentado um projeto de demonstração em que uma única base de código escrita em Rust funciona em todas as principais plataformas de GPU e CPU, incluindo CUDA, Vulkan (SPIR-V), Metal, DirectX 12, WebGPU e CPU
  • A programação tradicional para GPU costuma ser complexa e gerar duplicação por exigir linguagens separadas como GLSL e HLSL, mas esta demonstração dá suporte a todos os alvos de GPU usando apenas código Rust puro
  • Como principais implementações, integra os projetos Rust GPU (SPIR-V), Rust CUDA (NVVM IR) e Naga (camada de conversão de linguagens de GPU) — o mesmo algoritmo de ordenação bitônica roda na CPU e em todas as GPUs, e recursos da linguagem Rust como no_std, compilação condicional, Newtype, Enum e Trait também se aplicam diretamente ao código de GPU
  • Ainda restam pontos a melhorar, como integração oficial ao rustc, depuração e consistência de API, mas trata-se de uma tentativa que pode marcar um ponto de virada para a computação multiplataforma em GPU

Viabilizando computação multiplataforma em GPU com Rust

Introdução e significado do projeto

  • Com uma única base de código Rust, é possível executar o mesmo código de kernel em CUDA (NVIDIA), Vulkan (SPIR-V), Metal (Apple), DirectX 12 (Windows), WebGPU (navegador) e CPU
  • Sem usar linguagens dedicadas a shader ou kernel (como GLSL e HLSL), torna-se possível processar as mesmas operações em GPU e CPU usando apenas código Rust puro
  • Isso reduz drasticamente a duplicação de código e a complexidade entre GPU e CPU, além de levar as vantagens da linguagem e do ecossistema Rust — como segurança de tipos, testes, documentação e gerenciamento de build — também para a programação de GPU

Contexto

  • A programação tradicional de GPU exige o uso de linguagens de shader especializadas para cada plataforma (por exemplo: GLSL, HLSL, MSL, WGSL etc.)
  • Como consequência, o código de CPU e GPU fica separado, o que aumenta a duplicação de lógica e a complexidade de desenvolvimento
  • Para lidar com isso, a comunidade Rust vem buscando uma abordagem de compilar Rust comum tendo GPU como alvo
    • Rust GPU: compila código Rust para SPIR-V, executando em Vulkan e GPUs compatíveis com SPIR-V
    • Rust CUDA: compila código Rust para o IR do NVIDIA CUDA (NVVM IR, PTX), executando em CUDA
    • Naga: camada intermediária que oferece conversão entre várias linguagens de GPU (WGSL, SPIR-V, GLSL, MSL, HLSL), usada principalmente no projeto wgpu. É responsável pela portabilidade entre backends
  • Cada projeto começou de forma independente, com APIs e estruturas diferentes, mas a colaboração recente tornou possível a execução em GPU de uma base de código compartilhada

Como foi implementado

  • Na demonstração de exemplo, o algoritmo de ordenação bitônica foi implementado em um único código Rust, e o mesmo código roda em todos os alvos, GPU e CPU
  • Principais termos dos componentes
    • Host: código Rust executado na CPU (inicia o trabalho)
    • Device: GPU/CPU onde o kernel é realmente executado
    • Driver API: APIs de baixo nível para comunicação com o dispositivo, como CUDA, Vulkan, Metal e DX12
    • Backend: abstração em Rust sobre a Driver API alvo (cust, ash, wgpu etc.)
  • Combinando feature flags e alvos do Rust, é possível selecionar o backend e controlar o build
    • Ex.: opções de build independentes para cada backend, como wgpu, ash e cuda
    • Também é possível implementar uma estrutura que constrói vários backends em um único binário e os seleciona dinamicamente em tempo de execução

Fluxo de compilação do kernel

  • Dependendo do alvo e das features, os binários de execução em GPU (SPIR-V, PTX etc.) são gerados no build e embutidos no arquivo objeto
  • Em tempo de execução, o kernel embutido é carregado e, via Naga e afins, convertido para o formato exigido por cada plataforma antes de ser executado
  • Ex.:
    • macOS: SPIR-V convertido para MSL → execução em Metal
    • Windows: SPIR-V convertido para HLSL → execução em DX12
    • Linux/Android: SPIR-V → execução em Vulkan
    • CUDA: PTX compilado para SASS, enviado para a GPU e executado

Técnicas de programação para GPU amigáveis ao Rust

  • Suporte a no_std

    • Como a GPU não tem suporte de sistema operacional, o uso de no_std do Rust é indispensável
    • Como o ecossistema básico do Rust já assume ambientes sem SO, como embarcados, firmware e kernel, a GPU também pode ser suportada pelo modelo padrão do Rust sem precisar de um “modo especial” separado
  • Compilação condicional

    • A combinação de atributos cfg com feature flags permite escrever de forma clara e concisa o código que distingue plataformas e separa caminhos de GPU e CPU
    • IDE e compilador entendem todos os caminhos de código, garantindo alta confiabilidade e produtividade
  • Uso de newtype

    • Erros de mapeamento implícito de índices e structs, comuns em GPU, podem ser evitados no nível de tipos com o uso de newtype
    • Com o atributo #[repr(transparent)], é possível obter abstração de tipo sem overhead prático
  • Enum e representações seguras

    • Em vez de magic numbers, usa-se Enum do Rust, com #[repr(u32)] para garantir mapeamento para inteiros nativos
    • Pattern matching e exhaustiveness (tratamento de todos os casos) ajudam a montar código seguro
    • Porém, ao trocar valores de Enum por buffers compartilhados entre CPU e GPU, é preciso garantir que todos os valores sejam enums válidos
  • Algoritmos genéricos baseados em Trait

    • Com Trait, é possível abstrair operações de GPU como comparação, conversão e cálculo comuns a vários tipos de valor
    • Ao especificar claramente os trait bounds em algoritmos genéricos, concilia-se type safety e otimização
  • #[inline] e otimização de desempenho

    • O uso de #[inline] ajuda a fazer com que camadas de abstração desapareçam no resultado final da compilação
    • Considerando as características da GPU (desempenho, pilha limitada etc.), o projeto busca eliminar o custo da abstração
  • Composição de struct e agrupamento semântico

    • Parâmetros complexos de GPU são agrupados em struct por unidade de significado, garantindo segurança de tipos e evitando explosão de parâmetros
    • O padrão smart constructor bloqueia estados inválidos já na etapa de compilação
  • Layout de memória e controle com #[repr(C)]

    • Para compatibilidade de dados com a GPU, o layout da struct é definido explicitamente com #[repr(C)]
    • O texto menciona a necessidade futura de mais suporte da linguagem, como automatização de padding específico por GPU
  • Pattern matching

    • Como uma forma expandida de switch/case, permite tratar com clareza todas as ramificações e estados no código de GPU
    • O compilador pode checar caminhos de código e aplicar otimizações de desempenho
  • Funções genéricas

    • Permitem implementar a mesma lógica para vários tipos, sem depender do tipo de dado
    • Com trait bounds e monomorfização, melhoram manutenção e testabilidade
  • Macros derive

    • Implementam automaticamente traits adequados para GPU, como Copy, Clone, Debug, PartialEq e Pod
    • Isso aumenta a segurança e reduz boilerplate
  • Sistema de módulos e gestão de workspace

    • O sistema de pacotes e módulos do Rust organiza o código de host, kernels, tipos e fontes específicas de backend
    • Com Cargo workspace e workspace dependency, obtém-se consistência de dependências entre crates e maior facilidade de manutenção
  • Formatação, lint e documentação

    • Ferramentas padrão do Rust, como rustfmt e clippy, podem ser usadas exatamente da mesma forma para o código de GPU, mantendo qualidade consistente
    • Código de GPU também pode ser documentado com doc comments e cargo doc
  • Scripts de build

    • Com o build.rs do Cargo, é possível automatizar builds de kernel baseados em feature flags e o embedding dos binários
  • Testes unitários e produtividade no desenvolvimento

    • O código do kernel de GPU pode ser testado também na CPU, facilitando desenvolvimento e detecção de bugs
    • É possível usar ferramentas tradicionais como depuração com println e gdb/lldb
    • Kernels Vulkan também podem ser testados em CI com drivers de software como SwiftShader e lavapipe
    • Há integração fluida com ferramentas de terceiros para testes, medição de cobertura de código e testes baseados em propriedades

Experiência de desenvolvimento ainda incompleta e desafios

  • Como os backends de GPU ainda não estão embutidos oficialmente no compilador Rust, é necessário usar backends de codegen separados (spirv, nvvm) e fixar versões nightly
  • O alvo CUDA depende do LLVM 7.1 da NVIDIA, exigindo builds separados em distribuições Linux mais recentes
  • Ainda faltam uma boa experiência de build e depuração de kernel, além de melhor rastreamento de erros e informações de debug
  • As APIs do Rust GPU e do Rust CUDA, assim como os nomes de bibliotecas padrão, diferem entre si, causando confusão
  • No longo prazo, é necessário reforçar a consistência e a integração voltadas a GPU em toda a linguagem Rust e seu ecossistema

Participação da comunidade e futuro

  • A execução do mesmo código Rust em todas as principais plataformas de GPU já foi demonstrada
  • Daqui para frente, os desafios incluem melhorar build e depuração, ampliar o suporte da linguagem Rust e das APIs, e ajustar desempenho
  • Desenvolvedores que quiserem participar e contribuir podem consultar os repositórios GitHub de rust-gpu e rust-cuda

1 comentários

 
GN⁺ 2025-07-27
Comentários do Hacker News
  • É realmente impressionante que essa tecnologia seja possível
    Mas meu caso de uso é executar em hardware arbitrário de clientes, então tendo a não confiar em todas as camadas de abstração construídas sobre APIs de GPU
    O objetivo é aproveitar ao máximo os detalhes de baixo nível da GPU, e abordagens que tratam esses detalhes como incômodo acabam levando a bugs e perda de desempenho
    Isso porque cada alvo é significativamente diferente
    Para superar isso, acho que um sistema parecido teria de ser fornecido diretamente pelos próprios fabricantes
    Mas como ainda não há consenso entre eles, parece que as diferenças entre plataformas continuam grandes
    Em certos casos há exceções como o Angle, mas mesmo nesses casos a estabilidade só vem por meio de limitações de funcionalidade, o que no fim traz perda de desempenho
    Ainda assim, o fato de abordagens como compilação condicional serem possíveis claramente ajuda

    • Rust é uma linguagem de sistemas, então é possível ter tanto controle quanto se quiser
      Pretendemos refletir os detalhes e APIs de GPU na linguagem e nas bibliotecas core/std, e expor os recursos da GPU e do driver pelo sistema cfg()
      (sou o autor)

    • Penso exatamente a mesma coisa
      Sempre sou cauteloso ao construir algo comercial sobre camadas de abstração, adaptadores e camadas de tradução que talvez não recebam suporte suficiente no futuro
      Já estamos quase em 2025, mas ainda sinto falta desesperadamente de um padrão aberto que seja suportado por todos os fabricantes e permita usar todos os recursos do hardware de GPU mais recente
      Dado o estado atual das coisas, também parece significativo que a Nvidia, que criou a mais forte barreira de entrada no software, esteja sentada como representante da Khronos

    • Parece que você se importa bastante com desempenho, então queria perguntar por curiosidade
      Para mim, o estado atual das GPUs se parece muito com o que já vimos antes nas CPUs
      Nas CPUs, havia uma estrutura de compilador em três camadas, com otimização em uma camada intermediária e geração de código específico para cada hardware na camada final
      A vantagem disso é que a camada de abstração dura bastante tempo, e o compilador vai ficando mais inteligente com o tempo
      Fico curioso se algo assim é possível no lado das GPUs
      Ou se a diversidade das GPUs é grande demais para isso, tornando a ideia inviável ou antieconômica, ou ainda se esse é obviamente o caminho mas a tecnologia ainda não chegou lá

    • Exatamente isso
      Não vejo muito qual seria a vantagem de rodar Rust em GPUs da Nvidia em vez de simplesmente usar código CUDA existente
      Entendo adicionar abstrações, mas no fim fica com aquela sensação de “faz de tudo, mas de forma genérica”

    • Na verdade, tudo é abstração
      O próprio CUDA, no fim das contas, não passa de uma abstração que unifica conceitualmente hardwares que na prática são completamente diferentes

  • Eu desenvolvo apps de áudio nativos, e aqui cada ciclo de computação importa
    Além disso, preciso da API de compute completa, não apenas de shaders gráficos
    Fico curioso se o pipeline Rust -> WebGPU -> SPIR-V -> MSL -> Metal é sólido em termos de desempenho
    Parece frágil e imprevisível por ter etapas demais de transformação
    O mesmo vale para ... -> Vulkan -> MoltenVk -> ...
    Em contraste, Julia -> Metal pula o MSL e consegue aproveitar diretamente otimizações específicas do Apple Silicon, como Unified Memory
    A inovação aqui não é a linguagem de shader, e sim o uso de uma linguagem de programação completa, como Rust
    Rust oferece vários recursos como newtype, trait e macro

    • Ao usar rust-gpu, você não precisa necessariamente passar pela camada do WebGPU
      Isso porque rust-gpu é um backend de geração de código do compilador
      A estrutura permite compilar Rust MIR diretamente para SPIR-V

    • O pipeline Rust -> WebGPU -> SPIR-V -> MSL -> Metal é sólido em termos de desempenho?
      Em essência, é um conceito parecido com a forma como a Apple usa o Clang para otimização voltada a GPU
      SPIR-V é uma representação intermediária, como a IR usada no LLVM, então pode ser otimizada por sistema
      Em teoria, uma única base de código pode mirar todas as GPUs raster suportadas
      Já a stack Julia -> Metal tem portabilidade relativamente menor
      Para desenvolvedores limitados a uma plataforma, como em plugins de áudio, isso talvez não importe, mas para empresas multiplataforma como u-he ou Spectrasonics, um pipeline mais complexo baseado em SPIR-V pode ser mais atraente

    • Para computação numérica e otimização subsequente, Julia combina muito mais do que Rust, que é uma linguagem de sistemas
      Se você olhar a matriz de compatibilidade do Rust-CUDA, dá para ver que a demanda por Rust em programação CUDA é muito pequena
      Falta a maior parte dos recursos que as pessoas gostam no CUDA, e se houvesse demanda real haveria avanços maiores, mas não é o que acontece
      Parece que programadores de CUDA não têm muita vontade de usar Rust
      https://github.com/Rust-GPU/Rust-CUDA/blob/main/guide/src/features.md

  • Mesmo quando tenho código que eu gostaria de rodar na GPU, tudo em programação de GPU é tão doloroso que acabo desistindo
    Acho que a verdadeira utilidade do rust-gpu talvez seja transformar desenvolvedores de CPU em desenvolvedores de GPU, mesmo aceitando alguma perda de desempenho
    Se você já está confortável no mundo de GPU e domina cuda/vulkan/metal/dx, provavelmente não vai achar esse tipo de ferramenta tão atraente

  • Sou só um desenvolvedor web simples, então talvez seja uma pergunta idiota, mas nunca fiz programação de GPU
    Fico pensando se o WebGPU, por ser uma API única compatível com todos os backends de GPU, não resolveria todos esses problemas
    WebGPU também parece ser um dos backends suportados, então se for esse o caso, isso não seria só colocar outra abstração em cima de abstrações já existentes, acabando de qualquer forma em backends nativos de GPU?

    • Não é isso
      WebGPU é uma API que, do lado da CPU, controla a GPU para executar shaders e várias outras tarefas gráficas
      Já o Rust-GPU é uma linguagem para escrever o código de shader que realmente roda na GPU, de forma parecida com HLSL, GLSL e WGSL

    • Quando a Microsoft tinha mais influência, havia o DirectX
      Mas hoje em dia não sei até que ponto os fabricantes de GPU estão implementando APIs dedicadas para suas tecnologias proprietárias
      Existem vários recursos peculiares como DLSS, MFG e RTX
      Se eu fosse um vilão de desenho animado, poderia deixar as APIs existentes lentas de propósito e só oferecer desempenho de verdade em APIs novas e proprietárias do fornecedor
      Dito isso, eu também sou desenvolvedor web e não sei muito bem, mas pelo menos os LLMs acabam aprendendo esse tipo de coisa

    • Eu diria que WebGPU está mais para uma API de mínimo denominador comum
      Por exemplo, o editor Zed mira diretamente Metal na versão para Mac
      E também há divergência sobre o que exatamente significa “comum”
      Assim como OpenGL vs Vulkan, empresas influentes mostram movimentos para transformar seus próprios ecossistemas, como CUDA, Metal e DirectX, em padrão de mercado

    • Se fosse realmente tão simples assim, o CUDA não seria hoje o enorme fosso competitivo da Nvidia

    • A base importante desse projeto é o esforço da implementação WebGPU wgpu-rs
      WebGPU não é ideal para apps nativos
      Ele foi projetado com base em versões antigas do Vulkan, especialmente o período pré-RTX, e desde então as APIs nativas propriamente ditas evoluíram muito mais

  • Ainda está num nível meio tosco, mas é difícil acreditar que algo assim já seja possível
    Se esse tipo de avanço continuar, sinto que há potencial para quebrar a forte dependência de fornecedores no software de GPU e permitir competição real entre fabricantes de hardware
    Consigo até imaginar um mundo em que modelos de machine learning sejam escritos em Rust e executados tanto em Nvidia quanto em AMD
    Claro que, para obter desempenho máximo, seria preciso escrever código específico para cada fornecedor, mas isso é um problema de otimização
    Mesmo assim, ainda daria para usar kernels portáveis que rodam em múltiplas plataformas

    • Existe um framework de machine learning em Rust chamado https://burn.dev, com vários backends como CUDA e ROCm
      Pode valer a pena dar uma olhada

    • Acho difícil que, em menos de dez anos, exista um futuro em que modelos de machine learning sejam escritos em Rust e rodem tanto em Nvidia quanto em AMD
      Na prática, todo o ecossistema, como jax e torch, é baseado em Python
      Fazer todos os desenvolvedores profissionais migrarem para ferramentas em Rust é algo quase impossível até de imaginar

  • Se você contar as camadas de abstração

    1. código Rust específico do domínio
    2. abstração de backend sobre crates como cust, ash e wgpu
    3. abstração de plataforma, driver e API em wgpu e similares
    4. abstração de driver e plataforma em Vulkan, OpenGL, DX12 e Metal
    5. abstração, no driver, do hardware específico do fornecedor (e provavelmente há ainda mais dentro disso)
    6. hardware
      Existem pelo menos seis complexidades ocultas desse tipo
      Fico em dúvida se é possível atravessar todas essas camadas e ainda refletir as peculiaridades da plataforma no desempenho sem perdas
    • O que rust-gpu faz, no fim, é compilar para SPIR-V, ou seja, a IR do Vulkan
      Então as camadas 2 e 3 podem ser puladas ou mantidas em paralelo
      Ferramentas do ecossistema de desenvolvimento Rust como cargo, cargo test, cargo clippy e rust-analyzer também podem ser aproveitadas diretamente no desenvolvimento de shaders para GPU
      Na verdade, acho que a programação de GPU não é difícil porque a arquitetura da GPU seja alienígena demais, e sim porque todo o ecossistema é preso a fornecedores e a ferramentas antigas

    • A demo realmente se parece mais com uma máquina de Rube Goldberg complicada, mas isso acontece porque é a primeira vez que algo assim se torna possível
      Com o tempo, isso deve ficar mais natural e integrado
      E outra vantagem do ecossistema Rust é que você pode desenvolver com o nível de abstração ou de concretude que quiser
      Por exemplo, dá para usar recursos específicos de plataforma com std::arch, ou até escrever assembly
      Também é possível trocar alocadores e panic handlers, e quando o recurso externally implemented items, que está por vir, for ativado, a flexibilidade para lidar com camadas de abstração no nível desejado deve aumentar ainda mais

    • Bom ponto
      Mas as camadas 4 a 6 sempre existem também em shaders ou código CUDA
      As camadas 1 e 3, na prática, só seriam substituídas por outras camadas, especialmente em cenário multiplataforma
      Mesmo que esse projeto em Rust adicione uma camada de abstração, seria algo como apenas uma camada a mais
      E, como alguém que trabalha na prática justamente nas camadas 4 a 6, posso garantir que a complexidade escondida ali é enorme
      Para ser sincero, às vezes entram ainda mais camadas :P

    • De forma realista, a maioria dos usuários vai lidar no máximo até a camada (3) ou (4)
      Na prática, não é um aumento tão grande no número de etapas
      E, só para constar, acima da camada 6 também existem mais camadas de abstração
      Firmware e microarquitetura implementam o conjunto de instruções que imaginamos estar vendo

    • Não acho que isso seja tão diferente de operar compiladores e runtimes distintos para diferentes arquiteturas de CPU
      Há também convenções de chamada diferentes, diferenças de endianness, e no nível de hardware existem ainda firmware e microcódigo

  • É realmente impressionante que crates existentes em no_std + no alloc rodem na GPU quase sem alterações
    Dá a sensação de que isso abre muitas possibilidades de aplicação

    • Se o código foi escrito assumindo execução em CPU, o comportamento em termos de desempenho provavelmente será bem diferente
  • Muito impressionante
    A lista de projetos de Rust para GPU já ficou enorme
    Este aqui parece uma abstração de nível mais baixo que burn, e burn por sua vez é mais de baixo nível que candle
    Agora parece que falta só adicionar um backend como naga aos projetos acima
    Dá a sensação de que todo mundo está construindo algo sobre bases diferentes, mas talvez isso aconteça porque o trabalho com naga é relativamente recente
    Também quero acrescentar que o burn parece focar em suporte de plataforma
    Mas, pelo que vejo, o único backend que usa naga é o wgpu
    Então, no fim, não bastaria usar só wgpu?
    Resumindo, é wgpu/ash(vulkan, metal) ou então cuda
    Mais uma observação: outro crate próximo desse esforço
    https://github.com/tracel-ai/cubecl
    [0]: https://github.com/tracel-ai/burn
    [1]: https://github.com/huggingface/candle/

  • Fico em dúvida se isso é realmente “Rust” rodando na GPU
    Dando uma olhada por cima no código, parece uma estrutura em que, sobre a sintaxe de Rust carregada de macros de programação, acaba se apoiando uma linguagem de shader no final
    Como programação de GPU é algo muito diferente, acho que exige cuidado especial
    Colocar abstrações assim pode acabar inviabilizando certas otimizações

    • Na prática, a estrutura é que código Rust funcional mesmo seja compilado para bytecode SPIR-V
  • Fico muito feliz de ver esse projeto
    Tenho a sensação de que isso está ajudando bastante desenvolvedores que não querem se envolver em guerras de plataforma
    Exemplos como https://github.com/cogentcore/webgpu também são ótimos
    Eu uso golang e só quero conseguir aproveitar bem a GPU em todas as plataformas, e graças a isso dá para usar GPU em qualquer lugar
    Muito obrigado ao Rust