$30 off During Our Annual Pro Sale. View Details »

他言語がメインの場合のRustの活用法 - csbindgenによるC# x Rust FFI実践事例

他言語がメインの場合のRustの活用法 - csbindgenによるC# x Rust FFI実践事例

Rust.Tokyo 2023
https://rust.tokyo/2023/

Yoshifumi Kawai

October 21, 2023
Tweet

More Decks by Yoshifumi Kawai

Other Decks in Technology

Transcript

  1. 他言語がメインの場合のRustの活用法
    csbindgenによるC# x Rust FFI実践事例
    Rust.Tokyo 2023
    2023-10-21 Yoshifumi Kawai / Cysharp, Inc.

    View Slide

  2. About Speaker
    河合 宜文 / Kawai Yoshifumi / @neuecc
    Cysharp, Inc. - CEO/CTO
    株式会社Cygamesの子会社として2018年9月設立
    C#関連の研究開発/OSS/コンサルティングを行う
    Microsoft MVP for Developer Technologies(C#) since 2011
    CEDEC AWARDS 2022エンジニアリング部門優秀賞
    .NETのクラスライブラリ設計 改訂新版 監訳
    50以上のOSSライブラリ開発(UniRx, UniTask, MessagePack C#, etc...)
    C#では世界でもトップレベルのGitHub Star(合計30000+)を獲得

    View Slide

  3. 普段の開発
    C#によるクライアント/サーバーの開発言語統一がもたらす高効率な開発体制 ~プリコネ!グランドマスターズ開発事例~
    https://speakerdeck.com/cygames/sabanokai-fa-yan-yu-tong-gamotarasugao-xiao-lu-nakai-fa-ti-zhi-purikone-gurandomasutazukai-fa-shi-li
    つまり全部C#でやる
    Web Admin/Debug
    (C#, ASP.NET, Blazor)
    Unity(C#, iOS/Android)
    C# Server(ASP.NET, gRPC, MagicOnion)
    C# Batch, C# IaC, etc…
    C# Monorepo

    View Slide

  4. 普段のOSS
    https://github.com/Cysharp/
    (ほぼ)全てC#......

    View Slide

  5. Why Rust

    View Slide

  6. 原則なんでもC#でやる、が……
    ネイティブコードを利用すべき状況がある
    ・Android NDKなどネイティブAPIしかないものを利用したい
    ・Cなどで作られているネイティブライブラリを利用したい
    C++使う?
    C++は(特にクロスプラットフォームビルドが)厳しい……
    あと普通にC++書きたくない(?)
    Pure C#で移植する?
    モノによってはあまりにも大変すぎる……
    (パフォーマンス面ではC#に満足しているため、それは理由にない)
    (コード共有などの面も、クライアントもサーバーもWASMも全部C#でやるため普通に共有できている)

    View Slide

  7. 救いの手、Rust
    ネイティブコードの選択肢 C++ or Rust
    まともな標準パッケージマネージャー
    C++は無
    十分な開発環境(rust-analyzer, RustRover)
    総合的にはVisual Studio 2022でC++のほうが強力かも
    特にRustのデバッガー周りはかなり不満はある
    cargo buildのクロスプラットフォームコンパイル
    めっちゃサクッとできる、すごい!!!
    cc / cmakeクレートによる既存Cライブラリとの連携
    ビルドが全てRustで完結できて楽ちんすぎて、すごい!!
    bindgenによるC->Rust自動生成
    めっちゃ安定して生成できて、すごい!!!

    View Slide

  8. 救いの手、Rust
    ネイティブコードの選択肢 C++ or Rust
    まともな標準パッケージマネージャー
    C++は無
    十分な開発環境(rust-analyzer, RustRover)
    総合的にはVisual Studio 2022でC++のほうが強力かも
    特にRustのデバッガー周りはかなり不満はある
    cargo buildのクロスプラットフォームコンパイル
    めっちゃサクッとできる、すごい!!!
    cc / cmakeクレートによる既存Cライブラリとの連携
    ビルドが全てRustで完結できて楽ちんすぎて、すごい!!
    bindgenによるC->Rust自動生成
    めっちゃ安定して生成できて、すごい!!!
    総合的にはRustに大満足
    言語的に難しいという先入観というか風評があったため
    当初かなり躊躇ったのですが、やってみると意外とス
    ムーズに使えて良かった。C#と似てる部分も多いので
    (?)C#の人にもお勧め(?)
    ZigやC# NativeAOTという選択もモノによってはな
    くはなかったんですが、現在においてRustの完成
    度やエコシステムの充実度は群を抜いていた

    View Slide

  9. FFI & csbindgen

    View Slide

  10. csbindgen
    Rust to C#
    https://github.com/Cysharp/csbindgen
    bindgen(C to Rust), cbindgen(Rust to C)のようにRust to C#(cs)
    build.rsにコンフィグを書いてコンパイル時
    にsynで指定の.rsを解析して.csを生成する
    手書きバインディング作成はしんどいけど、生
    成系でいいものがなかったので作ってOSS公開

    View Slide

  11. C to Rust to C#
    bindgenが.hからC呼び出
    し用の.rs生成
    csbindgenが.rsを元にC公
    開用の.rsを生成
    csbindgenが.rsを元にRust
    呼び出し用の.cs生成
    ccでCライブラリをRustラ
    イブラリにリンク

    View Slide

  12. Concept of csbindgen
    ブラックボックスのないバインディング生成
    FFI可能なメソッドをRustで定義すると、1:1対応したC#が吐かれる
    FFI不可能なものを無理やり中間生成で自動対応させたりはしない
    SWIGなどが複雑な中間コードを生成するのが嫌だった
    凝った自動生成する必要はないのでマクロ(proc-macro)は不採用
    元コード(rs)は解析のみで弄らずC#生成
    FFIの境界面ではRust, C#ともにunsafeなコード
    Rustなのに安全じゃない!
    が、それがいい(?) unsafe

    View Slide

  13. Concept of csbindgen
    ブラックボックスのないバインディング生成
    FFI可能なメソッドをRustで定義すると、1:1対応したC#が吐かれる
    FFI不可能なものを無理やり中間生成で自動対応させたりはしない
    SWIGなどが複雑な中間コードを生成するのが嫌だった
    凝った自動生成する必要はないのでマクロ(proc-macro)は不採用
    元コード(rs)は解析のみで弄らずC#生成
    FFIの境界面ではRust, C#ともにunsafeなコード
    Rustなのに安全じゃない!
    が、それがいい(?)
    無理やりRustスタイルで安全にFFIできるように
    ブラックボックスな生成をするよりも、境界を
    超えることを表に出して、安全ではないコード
    を明示的に書かせたほうがむしろRustっぽくな
    いか……?境界面の唯一の共通言語はC(安全で
    はない)なのだから。
    境界近辺ではメモリもそれぞれC#
    で確保したメモリ、Rustで確保し
    たメモリと違うものが飛び交うの
    でunsafeに留意して慎重にやる
    クリーンな世界は一歩外側で
    クリーンな世界は一歩外側で

    View Slide

  14. Type Marshalling
    C# <-> Rust で 1:1 でマッピ
    ングできるのでかなり自然

    View Slide

  15. Case study

    View Slide

  16. MagicPhysX
    物理エンジンNVIDIA PhysX 5のC#バインディング
    https://github.com/Cysharp/MagicPhysX
    ・GUIアプリケーションの3D部分
    ・自作ゲームエンジンへの物理エンジン組み込み
    ・ディープラーニングのためのシミュレーション
    ・リアルタイム通信におけるサーバーサイド物理
    といった用途に使うことを想定して開発
    NVIDIAの公開しているPhysX 5(C++)ではなく、EmbarkStudioが公
    開しているphysx-rsをビルド元に使って、csbindgenでバインディ
    ングを生成した
    EmbarkStudioは、EA DICE
    でBattlefieldなどで使われ
    ている内製エンジン
    Frostbiteを作っていた人達
    が独立して立ち上げたスタ
    ジオで、Rustでゲームエン
    ジンを開発している!

    View Slide

  17. physx-rs
    C++/Rust is not easy
    C++のheaderをbindgenに
    投げてもうまくいかない
    cxx/autocxxのような取り
    組みもあるけれど……
    physx-rsはPhysXに特化して元の
    C++コードからC APIを公開する
    コードを自動生成して、それを
    通してC++とRustを繋げた
    An unholy fusion of Rust and C++ in physx-rs (Stockholm Rust Meetup, October 2019)
    https://www.youtube.com/watch?v=RxtXGeDHu0w
    非常に良いセッション
    でしたので必見
    そしてMagicPhysXはその成果に相乗
    りして、RustからC#をcsbindgenで
    自動生成して繋げた
    physx-rsがなかったら
    実現は難しかった!

    View Slide

  18. YetAnotherHttpHandler
    Unity用のHTTP/2(gRPC)クライアント
    https://github.com/Cysharp/YetAnotherHttpHandler
    C#のgRPCはPure C#実装(grpc-dotnet)でサーバー性能も結構良い
    が、Unityではランタイムが古くクライアントとして動作しない
    しかしgRPCは使いたい……
    そこでhyperとRustlsを使って
    HTTP/2(gRPC)通信部分をRustで実装
    win, osx, linux, android, iOS – x64, arm64
    C#のバインディング(csbindgen)と
    C# APIとしての高レベルAPIを提供
    0
    50000
    100000
    150000
    200000
    250000
    300000
    350000
    gRPC Implementation performance(2CPUs)
    Requests/sec(higher is better)
    https://github.com/LesnyRumcajs/grpc_bench/d
    iscussions/354

    View Slide

  19. YetAnotherHttpHandler
    Unity用のHTTP/2(gRPC)クライアント
    https://github.com/Cysharp/YetAnotherHttpHandler
    C#のgRPCはPure C#実装(grpc-dotnet)でサーバー性能も結構良い
    が、Unityではランタイムが古くクライアントとして動作しない
    しかしgRPCは使いたい……
    そこでhyperとRustlsを使って
    HTTP/2(gRPC)通信部分をRustで実装
    win, osx, linux, android, iOS – x64, arm64
    C#のバインディング(csbindgen)と
    C# APIとしての高レベルAPIを提供
    0
    50000
    100000
    150000
    200000
    250000
    300000
    350000
    gRPC Implementation performance(2CPUs)
    Requests/sec(higher is better)
    https://github.com/LesnyRumcajs/grpc_bench/d
    iscussions/354
    大部分をPure C#で書く, TLS部分をOpenSSLなどを使うという案があったが、
    「Pure C#で書くのは大変」「OpenSSLなどをクロスプラットフォームビル
    ドするのが大変」といった問題があった。
    RustはPure Rustによる素晴らしいライブラリ郡がある&それらはクロスプ
    ラットフォームビルドがやりやすい&csbindgenはRustからのコード生成を
    サポートする、といった形によってRustライブラリのC#利用を可能にした

    View Slide

  20. NativeCompressions
    LZ4 / Zstandardの.NETバインディング
    (開発が今日までに間に合わなかったのでOSS化はまだ!)
    ・伸縮が爆速のLZ4
    ・バランスよく高性能なZStandard
    Cライブラリの両者をRustのCC/CMakeでビルドしてcsbindgenを通
    してC#から呼び出す
    圧縮ライブラリは通常Pure C#実装するし、基本的にパフォーマン
    スも問題ない、のだけれど……
    特にZStandardは頻繁なバージョンアップのたびに性能が向上してい
    る。C#移植すると、移植時のコードで固定されてしまい、その後の
    性能向上の最適化コードにはついていけない、結果、ネイティブラ
    イブラリと性能が乖離していってしまう。
    最高の性能をC#に持ち込むために
    ネイティブバインディングを選択。
    クロスプラットフォームビルドも
    Rust経由で随分やりやすくなった。

    View Slide

  21. Conclusion

    View Slide

  22. C# ❤ Rust
    C#の可能性をRustで広げ切り開く
    C, C++, RustのライブラリをC#に持ち込んで実証した
    RustはC#/* 任意の言語名を入れてください */ を補完してくれる
    csbindgenはよくできてる
    自画自賛!作ってよかった!
    これのお陰でC#とRustの相性が無限大に!
    syn crateでRustのSyntax Treeを解析するのはやりやすくて快適
    C#的には普段Roslyn(C# Compiler)でC#のSyntax Tree解析してるので……
    Rust AnalyzerはRoslynで開発されたRed Green Treeを使っていたりなのでより馴染み深い(?)
    C# ❤ Rustの世界観を深めるため開発していきます……!

    View Slide

  23. View Slide