- Rustで書かれた単一のコードベースが、CUDA, Vulkan(SPIR-V), Metal, DirectX 12, WebGPU, CPU など、主要なGPUおよびCPUプラットフォームすべてで動作するデモプロジェクトが公開
- 従来のGPUプログラミングでは、GLSL, HLSL など別個の言語を使う必要があり、複雑さや重複の問題があったが、このデモでは 純粋なRustコードだけで全GPUターゲットをサポート
- 主要実装として Rust GPU(SPIR-V), Rust CUDA(NVVM IR), Naga(GPU言語変換レイヤー) プロジェクトを統合し、同一のビトニックソートアルゴリズムがCPUとすべてのGPUで動作し、Rustの no_std, 条件付きコンパイル, Newtype, Enum, Trait などの言語機能 がGPUコードにもそのまま適用される
- まだ公式 rustc への統合、デバッグ、API一貫性など 改善課題 は残るものの、GPUクロスプラットフォームコンピューティングの転換点 となる試み
RustベースGPUクロスプラットフォームコンピューティングの実現
プロジェクト紹介と意義
- 単一のRustコードベース で CUDA(NVIDIA), Vulkan(SPIR-V), Metal(Apple), DirectX 12(Windows), WebGPU(ブラウザ), CPU などで同一のカーネルコードを実行
- シェーダーまたはカーネル専用言語(GLSL, HLSL など)を使わず、純粋なRustコード だけでGPUおよびCPUで同一の演算を処理可能
- これによりGPU・CPU間のコード重複と複雑さを大幅に減らし、Rustエコシステムのツールと言語の利点(型安全性、テスト、ドキュメント化、ビルド管理など)をGPUプログラミングにも適用できる
背景
- 従来のGPUプログラミングでは、各プラットフォームごとに特化したシェーダー言語(例: GLSL, HLSL, MSL, WGSL など)の使用が必須
- その結果、CPUとGPUのコードが分離され、ロジックの重複や開発の複雑化を招いていた
- Rustコミュニティはこれに対応するため、通常のRustをGPU向けにコンパイル するアプローチを追求している
- Rust GPU: RustコードをSPIR-Vにコンパイルし、VulkanおよびSPIR-V互換GPUで動作
- Rust CUDA: RustコードをNVIDIA CUDA向けIR(NVVM IR, PTX)にコンパイルし、CUDAで実行
- Naga: さまざまなGPU言語(WGSL, SPIR-V, GLSL, MSL, HLSL)間の変換を支援する中間レイヤー(主にwgpuプロジェクトで使用)。バックエンド移植性を担う
- 各プロジェクトは独立して始まりAPIや構造も異なっていたが、最近の協力によって 共通コードベース のGPU実行が実現した
実装方式
- サンプルデモでは ビトニックソートアルゴリズム を単一のRustコードで実装し、GPUとCPUのすべての対象で同じコードが実行される
- 主要コンポーネント用語
- Host: CPU上で実行されるRustコード(処理開始)
- Device: カーネルが実際に実行されるGPU/CPU
- Driver API: CUDA, Vulkan, Metal, DX12 など、デバイスと通信するための低レベルAPI
- Backend: RustからDriver API向けの抽象化を提供(cust, ash, wgpu など)
- Rustの featureフラグ とターゲットの組み合わせにより、バックエンド選択およびビルド制御が可能
- 例: wgpu, ash, cuda など、各バックエンドごとの独立したビルドオプションを提供
- 複数のバックエンドを1つのバイナリにビルドし、ランタイムで動的に選択する構成も実装可能
カーネルのコンパイルフロー
- ターゲットおよびfeatureに応じて、GPU実行バイナリ(SPIR-V, PTX など)をビルド時に生成し、オブジェクトファイルに埋め込む
- ランタイムで埋め込まれたカーネルをロードし、Naga などを通じてプラットフォームごとの要求フォーマットに変換して実行
- 例:
- macOS: SPIR-VをMSLに変換→Metalで実行
- Windows: SPIR-VをHLSLに変換→DX12で実行
- Linux/Android: SPIR-V→Vulkanで実行
- CUDA: PTXをSASSにコンパイルし、GPUへアップロードして実行
RustフレンドリーなGPUコーディング手法
-
no_std のサポート
- GPUにはOSサポートがないため、Rustの
no_std 使用が必須
- Rustの基本エコシステムがもともと組み込み、ファームウェア、カーネルなどOSのない環境を想定しているため、GPUも別個の「特殊モード」なしでRust標準の方法でサポート可能
-
条件付きコンパイル
- cfg属性とfeatureフラグの組み合わせにより、プラットフォーム別コードやGPU/CPU別コードを明確かつ簡潔に書ける
- IDEとコンパイラがすべてのコードパスを理解できるため、高いコード信頼性と生産性を確保
-
newtype の活用
- GPUで起こりがちな暗黙的なインデックス・構造体マッピングのミスを、newtypeの使用により型レベルで防止
#[repr(transparent)] 属性により、実質的なオーバーヘッドなしで型抽象化が可能
-
Enum と安全な表現
- マジックナンバーの代わりにRustのEnumを使い、
#[repr(u32)] を適用してネイティブ整数との対応を確保
- パターンマッチングと exhaustiveness(全ケース分岐処理) によって安全なコードを構成
- ただし、CPU-GPU間の共有バッファでEnum値をやり取りする際は、すべての値が正しいEnumである必要があるため注意が必要
-
Traitベースのジェネリックアルゴリズム
- Traitを使って、さまざまな値型に対する共通の比較、変換、演算などGPU演算の抽象化を提供
- ジェネリックアルゴリズムに trait bound を明確に指定し、type safety と最適性を両立
-
#[inline] と性能最適化
#[inline] の使用により、抽象化レイヤーが実際のコンパイル結果から消えるよう促す
- GPUの特性(性能、スタック不足など)を踏まえ、抽象化コストが発生しないよう設計されている
-
Struct の組み合わせと意味論的グループ化
- 複雑なGPUパラメータを意味単位でまとめてstructとして管理し、型安全性を確保しつつパラメータ爆発を防ぐ
- Smart constructor パターンにより、invalid状態をコンパイル段階で遮断
-
メモリレイアウトと #[repr(C)] 制御
- GPUとのデータ互換性のため、
#[repr(C)] によって構造体レイアウトを明示的に指定
- 将来的にはGPUごとのpadding自動化など、追加の言語サポートの必要性にも言及
-
パターンマッチング
- switch/caseを拡張した概念として、GPUコード内のすべての分岐と状態を明確に処理できる
- コンパイラによるコードパス検査と性能最適化が可能
-
ジェネリック関数
- データ型に依存せず、複数の型に対して同一ロジックを実装
- trait bound, モノモーフィゼーションなどにより、保守性とテスト容易性を強化
-
Deriveマクロ
- Copy, Clone, Debug, PartialEq, Pod などGPU向けに適したTraitを自動実装
- 安全性を保ちつつボイラープレートを最小化
-
モジュールシステム、ワークスペース管理
- Rustのパッケージ/モジュールシステムにより、ホストコード、カーネル、型、バックエンド別のソースを構造化
- Cargo workspace, workspace dependency によってクレート間依存の一貫性と保守性を確保
-
フォーマット、Lint、ドキュメント化
- rustfmt, clippy などRust標準ツールとまったく同じ方法でGPUコードを管理でき、一貫した品質を維持可能
- Doc comment, cargo doc によりGPUコードも文書化可能
-
ビルドスクリプト
- Cargoの build.rs を通じて、feature flag ベースのカーネルビルドとバイナリ埋め込みを自動化
-
ユニットテストと開発生産性
- GPUカーネルコードをCPU上でもテストできるため、開発とバグ検出が容易
- println デバッグ、gdb/lldb など従来ツールも利用可能
- Vulkanカーネルもソフトウェアドライバ(SwiftShader, lavapipe)でCIテスト可能
- テストやコードカバレッジ測定、property-basedテストなどサードパーティーツールとも円滑に連携
未完成の開発者体験と課題
- GPUバックエンドが公式Rustコンパイラに組み込まれておらず、別個のcodegenバックエンド(spirv, nvvm)の使用とnightly版固定が必要
- CUDAターゲットはNVIDIAのLLVM 7.1に依存しており、最新のLinuxディストリビューションでは別途ビルドが必要
- カーネルのビルド・デバッグ体験が不足しており、エラートレースやデバッグ情報も不十分
- Rust GPUとRust CUDAではAPIや標準ライブラリ名称などが異なり、混乱を招く
- 長期的にはRust言語とエコシステム全体で、GPU中心の一貫性と統合性の強化が必要
コミュニティ参加と未来
- Rustにより、すべての主要GPUプラットフォームで同一コードを実行できるようになった
- 今後はビルドとデバッグの改善、Rust言語およびAPIサポートの拡大、性能チューニングなどが課題として残っている
- 参加や貢献を望む開発者は、rust-gpu, rust-cuda のGitHubリポジトリを参照するとよい
1件のコメント
Hacker Newsのコメント
この技術が実現できるという点は本当に印象的。 ただ、私のユースケースは任意のクライアントハードウェア上で実行することなので、GPU APIの上に構築されたあらゆる抽象化レイヤーはあまり信用していない。 目的はGPUの低レベルな詳細を最大限活用することで、そうした詳細を面倒なものとして扱うアプローチはバグや性能低下につながる。 ターゲットごとに意味のある違いがあるからだ。 これを克服するには、似たような仕組みをベンダー自身が直接提供する必要があると思う。 ただ、ベンダー間でまだ足並みがそろっていないため、プラットフォームごとの差は大きいように見える。 特定のケースでは Angle のような例外もあるが、そうした場合も機能制限によってしか安定性を得られず、結果として性能損失がある。 それでも、条件付きコンパイルのようなアプローチが可能だという点は間違いなく助けになる。
Rustはシステム言語なので、望むだけの制御権を持てる。 私たちはGPUの詳細やAPIを言語と core/std ライブラリに反映し、GPUやドライバの機能を
cfg()システムを通じて公開する予定。 (作者です)私もまったく同感。 将来的に十分なサポートを受けられるか分からない抽象化レイヤー、アダプタ、変換レイヤーの上に商用の何かを構築するのは、いつも慎重になってしまう。 2025年が目前なのに、いまだにすべてのベンダーがサポートし、最新GPUハードウェアの全機能を使えるオープンスタンダードが切実に必要だと感じる。 現状がこうであるうえに、最も強力なソフトウェア参入障壁を築いた Nvidia が Khronos の代表の位置にいる現実は示唆的だ。
性能に強い関心があるようなので聞いてみたい。 私には、GPU分野の今の姿は昔のCPUにかなり似て見える。 CPUでは3段階のコンパイラ構造で、中間層で最適化したあと、最終層で各ハードウェア向けのコードを出力する方式だった。 この構造の利点は、抽象化レイヤーが長持ちし、コンパイラが時間とともに賢くなっていくことだ。 GPU側でもこういう構造は可能なのだろうか。 それともGPUの多様性が大きすぎて不可能または不経済なのか、あるいは当然そちらに向かう流れなのに、まだ実現していないだけなのか気になる。
まさにその通り。 Nvidia GPUでわざわざRustを動かすことが、既存のCUDAコードより何が優れているのかよく分からない。 抽象化を追加すること自体は理解できるが、結局のところ「何でも一応できる寄せ集め」のような印象がある。
実際のところ、すべては抽象化だ。 CUDAも、実際には互いにまったく異なるハードウェアを概念的に統一する抽象化にすぎない。
私はネイティブのオーディオアプリを開発していて、ここではすべての演算サイクルが重要。 また、グラフィックシェーダーではなく完全なコンピュートAPIが必要だ。
Rust -> WebGPU -> SPIR-V -> MSL -> Metalパイプラインが性能面で堅牢なのか気になる。 変換段階が多すぎて、脆く予測不能に見える。... -> Vulkan -> MoltenVk -> ...も同様だ。 一方でJulia -> Metalは MSL を飛ばし、Apple Silicon の特殊な最適化(例: Unified Memory)を直接活用できる。 ここでの革新はシェーダー言語ではなく、Rustのような完全なプログラミング言語を使う点だ。 Rustは newtype、trait、macro などさまざまな機能をサポートしている。rust-gpu を使う際、必ずしも WebGPU レイヤーを経由する必要はない。 rust-gpu はコンパイラのコード生成バックエンドだからだ。 Rust MIR を直接 SPIR-V にコンパイルできる構造になっている。
Rust -> WebGPU -> SPIR-V -> MSL -> Metalパイプラインは性能面で堅牢ですか? 基本的にはGPU向けの Apple の Clang 最適化方式に近い発想だ。 SPIR-V は LLVM で使われる IR のような中間表現なので、システムごとに最適化できる。 理論上は、1つのコードベースでサポートされるすべてのラスタGPUをターゲットにできる。 一方で Julia -> Metal スタックは相対的に移植性が低い。 オーディオプラグインのようなプラットフォーム限定の開発者には関係ないが、u-he や Spectrasonics のようなクロスプラットフォーム開発会社にとっては、複雑な SPIR-V ベースのパイプラインの方が魅力的かもしれない。数値計算とその後続の最適化に関しては、Julia の方がシステム言語である Rust よりずっと向いている。 Rust-CUDA の互換性マトリクスを見ると、CUDAプログラミングにおける Rust 需要が非常に少ないことが分かる。 人々が好む CUDA の機能の大半が欠けており、本当に需要が大きかったならもっと進展していてもよかったはずだが、そうなっていない。 CUDAプログラマは Rust を使う意欲があまりないように見える。 https://github.com/Rust-GPU/Rust-CUDA/blob/main/guide/src/features.md
私のようにGPUで動かしたいコードがあっても、GPUプログラミングは何もかも苦痛なので結局やらなくなってしまう。 rust-gpu の本当の用途は、ある程度の性能低下を受け入れてでも CPU 開発者を GPU 開発者に変えることなのではないかと思う。 すでにGPUに慣れていて cuda/vulkan/metal/dx を熟知しているなら、こうしたツールにはあまり魅力を感じないだろう。
私はただのWeb開発者なので、もしかしたら間抜けな質問かもしれないが、GPUプログラミングをしたことがない。 WebGPU がすべてのGPUバックエンドに互換な単一APIなのだから、こうした問題は全部解決しているのではないかと気になる。 WebGPU もサポートされるバックエンドの1つのようだが、もしそうなら既存の抽象化の上にさらに別の抽象化を載せて、結局ネイティブGPUバックエンドを呼び出すだけではないのか?
そうではない。 WebGPU は D3D、Vulkan、SDL GPU のように、CPUからGPUを制御してシェーダー実行などさまざまなグラフィック処理を行わせるAPIだ。 Rust-GPU は HLSL、GLSL、WGSL に近い形で、GPU上で実際に実行されるシェーダーコードを書ける言語だ。
Microsoft に影響力があった頃は DirectX があった。 だが今では、GPUメーカーがそれぞれ独自技術向けの専用APIをどの程度実装しているのか、よく分からない。 DLSS、MFG、RTX など独特な機能がいろいろある。 もし漫画映画の悪役だったら、既存APIをわざと遅くし、新しくて高速なベンダー専用APIだけを速く提供することもできるだろう。 ちなみに私もWeb開発者なので詳しくはないが、少なくとも LLM はこういう内容を学習することになる。
WebGPU は最小公倍数的なAPIに近いと思う。 たとえば Zed エディタは Mac 版で Metal を直接ターゲットにしている。 そして「共通」が何を意味するかについても、人によって意見が違う。 OpenGL と Vulkan のように、力のある企業は CUDA、Metal、DirectX など自分たちのエコシステムを市場標準にしようとする動きを見せる。
本当にそれほど簡単なら、CUDAがいま Nvidia の強力な堀の役割を果たしてはいなかったはずだ。
このプロジェクトでは wgpu-rs の WebGPU 実装における努力が重要な基盤になっている。 WebGPU はネイティブアプリには最適ではない。 Vulkan の古いバージョン(特に pre-RTX)をもとに設計されたため、その後に本格化したネイティブAPIははるかに進化している。
まだ粗削りな段階だが、こういうことが可能だというのは本当に信じがたい。 今後こうした進歩が続けば、GPUソフトウェアにおける深刻なベンダーロックインを打ち破り、ハードウェアメーカー同士の本当の競争が可能になるかもしれないという潜在力を感じている。 いつか機械学習モデルを Rust で書いて Nvidia と AMD の両方で動かせる世界が来るかもしれないと想像している。 もちろん最高性能を出すには各ベンダーに特化したコードを書く必要があるだろうが、それは最適化の問題だ。 それでもクロスプラットフォームで動く移植性の高いカーネルを使える。
https://burn.dev という Rust の機械学習フレームワークがあり、CUDA や ROCm などさまざまなバックエンドを持っている。 参考になるかもしれない。
機械学習モデルを Rust で書いて Nvidia と AMD の両方で動かせる未来は、10年以内では難しそうだ。 現実的には jax や torch など生態系全体が Python ベースだ。 現場の開発者全員を Rust ツールへ乗り換えさせるのは、ほとんど想像もつかないほど難しい。
抽象化レイヤーを数えると、
rust-gpu がやっていることは、結局 SPIRV(つまり Vulkan の IR)へコンパイルすることだ。 だから 2 と 3 のレイヤーは省略したり、並列構造にしたりすることもできる。 Rust の開発エコシステムにある cargo、cargo test、cargo clippy、rust-analyzer のようなツールも、そのままGPUシェーダー開発に活用できる。 実際にはGPUアーキテクチャが異様すぎるからGPUプログラミングが難しいのではなく、生態系全体がベンダーロックインされ、古いツールに足を引っ張られている結果だと思う。
デモは確かに複雑なルーブ・ゴールドバーグ・マシンに近いが、その理由はこうしたことが初めて可能になった段階だからだ。 時間がたてば、もっと自然で統合的なものへ変わっていくだろう。 そして Rust エコシステムのもう1つの利点として、望むだけ抽象化も具体化もできる。 たとえば
std::archでプラットフォーム固有機能を使ったり、アセンブリを書いたりもできる。 アロケータや panic ハンドラの差し替えも可能で、まもなく登場する externally implemented items 機能が有効になれば、抽象化レイヤーを思い通りに扱える柔軟性はさらに高まるはずだ。いい指摘だ。 ただ、4〜6段階のレイヤーはシェーダーやCUDAコードにも常に存在する。 1と3のレイヤーも、実際には別のレイヤーに置き換わるだけだ(特にクロスプラットフォームなら)。 この Rust プロジェクトが抽象化レイヤーを追加するとしても、せいぜい1段階くらいだ。 そして 4〜6段階の実務担当者として言うが、その中にも隠れた複雑性はとてつもなく多い。 正直、さらに多くのレイヤーが入ることもある :P
現実的に見れば、たいていのユーザーは最大でも (3) か (4) のレイヤーまでしか扱わない。 実際にはそれほど大きな追加段階が増えるわけではない。 ちなみに6段階の上にもさらに抽象化レイヤーはある。 ファームウェアやマイクロアーキテクチャが、私たちの考える命令セットを実装している。
CPUアーキテクチャごとに異なるコンパイラやランタイムを運用するのと、そこまで違わないと思う。 それぞれ異なる呼び出し規約やエンディアンの違いもあるし、ハードウェアレベルではファームウェアやマイクロコードまである。
既存の no_std + no alloc クレートが、ほとんど修正なしでGPU上で動くという点には本当に驚かされる。 これは本当にさまざまな応用アイデアを開く感じがする。
本当にすごい。 もう Rust GPU プロジェクトの一覧もかなり大きくなっている。 今回のものは burn よりさらに低レベルの抽象化に近く、burn は candle より低レベルだ。 あとは naga のようなバックエンドを上のプロジェクト群に追加することくらいだろう。 みんな別々の基盤の上に何かを作っているようにも見えるが、naga の作業が比較的最近だからかもしれない。 burn がプラットフォーム対応に注力している点も付け加えておきたい。 しかし見る限り、naga を使っている唯一のバックエンドは wgpu だ。 すると結局 wgpu だけ使っていれば十分なのではないか? 要するに wgpu/ash(vulkan, metal) か、あるいは cuda だ。 追加の小ネタとして、この取り組みに近い別のクレートもある。 https://github.com/tracel-ai/cubecl [0]: https://github.com/tracel-ai/burn [1]: https://github.com/huggingface/candle/
これが本当に「Rust」がGPU上で動いていると言えるのか疑問だ。 コードをざっと見ると、プログラミングマクロが大量に入った Rust 構文の上に、最終的にシェーダー言語を載せた構造に見える。 GPUプログラミングはあまりに特殊なので、特別な注意が必要だと思う。 こうして抽象化を入れると、特定の最適化が不可能になるかもしれない。
このプロジェクトを見て本当にうれしい。 プラットフォーム争いに巻き込まれたくない開発者にとって大きな助けになっていると感じる。 https://github.com/cogentcore/webgpu のような例も良い。 私は golang を使っていて、すべてのプラットフォームでGPUがうまく活用できればそれでいいのだが、こうした取り組みのおかげでGPUをどこでも使えるようになる。 Rust に本当に感謝している。