1 ポイント 投稿者 GN⁺ 2025-05-23 | 1件のコメント | WhatsAppで共有
  • Rustで書かれた rav1d AV1デコーダが、Cベースの dav1d と比べて約9%遅いことが判明
  • バッファ初期化の最適化 と構造体比較ロジックの改善により、それぞれ個別に 1.5%、0.7% の高速化効果を確認
  • プロファイリングツール samply を活用し、2つのバージョンの性能差の原因を具体的に特定
  • Rustの PartialEq のデフォルト実装の代わりにバイト単位比較方式を用いて効率を向上
  • 今回の最適化で全体の性能差の約30%を改善したが、まだ最適化の余地が残っている

背景とアプローチ

  • rav1d は、dav1d AV1デコーダを c2rust で Rust に移植し、asm 最適化関数と Rust 言語特有の安全性改善を反映したプロジェクト
  • 公開されたベースライン性能指標が定められており、Rustベースの rav1d は Cベースの dav1d より約5%遅い状態
  • 複雑なビデオデコーダ全体の構造ではなく、同一入力におけるバイナリ実行時間の差に焦点を当てて分析
  • 性能測定ツール(hyperfine) とプロファイラ(samply) で体系的に比較
  • 対象環境は macOS M3 チップで、単一スレッド実行に単純化

性能測定: 初期値の比較

  • 同一のテストファイル(Chimera-AV1-8bit-1920x1080-6736kbps.ivf)でそれぞれビルドとベンチマークを実施
  • rav1d: 約73.9秒、dav1d: 約67.9秒で、約6秒(9%)の実行時間差を確認
  • 各コンパイラ(Clang, Rustc) はほぼ同一の LLVM バージョンを使用

プロファイリング分析

  • samply プロファイラで各実行ファイルの関数単位サンプル数を比較
  • NEON(ARM SIMD) ベースのアセンブリ関数の呼び出し経路とサンプル分布を重点的に確認
  • dav1d は別個のフィルタ関数に分離して asm 関数を分岐呼び出しし、rav1d は1つのディスパッチ関数で全てを管理
  • cdef_filter_neon_erased 関数の Self サンプル数は、dav1d の2関数の合計より約270個多く、全体の1%に相当する差が見られた
  • 分析の結果、一時バッファ(zero-initialized buffer) が不必要に大きく初期化されている区間を特定

バッファ初期化削減の最適化

  • Rust は安全性のため、[0u16; LEN] のような形で自動的に zeroing を行う
  • しかし C(dav1d) はバッファを明示的に zeroing せず、実際に使う区間にだけ値を書き込む
  • Rust では std::mem::MaybeUninit を使って不要な初期化コストを削除
  • cdef_filter_neon_erased 関数の Self サンプルは 670 個から 274 個へ大きく減少
  • もう1つの大容量 Align16 バッファも、初期化をループの外に hoist して初期化コストを1回に削減
  • 最適化後のベンチマークは約72.6秒となり、1.2秒(1.5%)改善

構造体比較の最適化

  • プロファイリングの inverted stack 分析で、add_temporal_candidate 関数が予想以上に非効率に動作していることを発見
  • この関数内の Mv 構造体のフィールド比較(PartialEq 自動実装) が、不必要に遅いコードを生成していた
  • C では union を使って uint32_t 単位の効率的な比較を実施
  • Rust では unsafe を避けつつ、zerocopy::AsBytes トレイトでバイトスライス単位の比較を実装
  • この最適化でもさらに 0.5秒(約0.7%) の性能向上を達成

結果とまとめ

  • 2つのシンプルな最適化(バッファ初期化削減、構造体のバイト比較) により、実行時間を約2%以上短縮
  • それでもなお約6%の性能差が残っており、追加の最適化余地は大きい
  • プロファイラのスナップショット比較手法が効果的であることを確認
  • rav1d と dav1d のスナップショット分析に基づく追加最適化の可能性は高い
  • プロジェクトメンテナたちの積極的なフィードバックと協力により、安全性を損なわずに改善を実現

要約

  • プロファイラ(samply) とベンチマーク(hyperfine) ツールを使い、rav1d と dav1d の6秒(9%)の実行時間差を精密に分析
  • 主な最適化は2つ:
    • ARM 特化コードで不要なバッファ zeroing を削除(1.2秒, -1.6%)
    • 小さな数値構造体の PartialEq 実装を高速なバイト比較に変更(0.5秒, -0.7%)
  • 新たな unsafe コードなしで、各最適化は数十行以内に収まる簡潔なもの
  • メンテナとの協業と PR レビューを通じて、信頼性と品質の向上も同時に達成
  • なお約6%の性能差が残っており、さらなる profiler ベースの比較最適化を研究する余地は十分にある

ぜひ試してみてほしい! rav1d が最終的に dav1d より速くなるかもしれない 👀🦀。

1件のコメント

 
GN⁺ 2025-05-23
Hacker Newsのコメント
  • u16 を2つ比較する問題が興味深いテーマだという意見が共有され、関連イシューへのリンクが示されている https://github.com/rust-lang/rust/issues/140167
    • 議論で store forwarding が言及されていないのは意外だという驚き、-O3 でのコード生成結果はやりすぎだが -O2 では妥当な判断だという見方、構造体の一方が演算直後であれば 32ビット読み込みを試みた際に store forwarding が失敗し、性能向上が無意味になり得るという具体的な説明、非インライン・非 PGO の状況ではコンパイラが最適化の適否判断に必要な情報を欠いているという指摘
    • イシューの議論が単なる「自分も遭遇した」「いつ直るのか」といった類いのコメントで埋まっていないのが良いという感想、ウェブ開発者として GitHub Issues には不満があるという率直な意見の共有
    • この事例はコンパイラ開発がいかに複雑かを示す例だという意見、C 系コンパイラもこうした問題を特別うまく扱えるわけではないだろうという確信
  • ブログ記事にプロファイラの結果をどう埋め込んだのか気になるという声、HTML ノードをそのままコピーしたのかという質問
  • バッファ初期化(ゼロ埋め)の省略による性能上の利点に関する記事が、数日前の関連エントリに続いて投稿されたのが興味深いという感想、過去記事へのリンク共有 https://news.ycombinator.com/item?id=44032680
  • 本文タイトルは実際の成果に比べて控えめすぎるという指摘、実際には2つの良い最適化によって 2.3% の高速化があったことを強調
    • 1.5% の改善は aarch64 にしか当てはまらないため、それを全体の数値として挙げるのはやや公平ではないという意見、arm/x86 の比率を考えると半分程度と見るのが妥当だという主張
  • 投稿は有益で、16ビット整数ペア比較の非効率なコード発見が印象的だったという評価
    • Rust/LLVM 開発者が、この最適化を適用可能な場合に自動適用できるのか気になるという声、Rust ではメモリ初期化に関する情報がはるかに正確だという言及
  • すべての条件が同じなら、この種のコーデックは Rust より WUFFS のような言語、あるいはそれに準ずる特化言語で扱うべきだと思うという意見、dav1d のような複雑なコードを WUFFS に変換するのは既存の C コードの翻訳(clean up)よりはるかに難しいという実感の共有、それでもこうした試みには価値があり、文明的な観点から投資に値するという主張
    • WUFFS は Matroska、webm、mp4 のようなコンテナ解析には向いているが、ビデオデコーダにはまったく向いていないという説明、動的メモリ割り当てがないため動的データ処理が難しいこと、ビデオコーデックは単にファイルを解析するだけでなく、非常に多様な動的状態管理を必要とすることの強調
  • rav1d の懸賞金の進捗が気になって独り言のように尋ねていたが、同じように気にしている人がいることへの共感
  • 面白いミームで始まる記事は良い投稿だと分かるという感想、最近話題になった「Rav1d AV1 Decoder Rust 最適化 2万ドル懸賞金」の議論との関連に言及し、関連リンクを追加 https://news.ycombinator.com/item?id=43982238
    • これは明確な「Nominative determinism(名前が運命を決める効果)」のケースだという、ウィットを交えた意見
  • 正直なところ、最初の最適化は perf をきちんと使えば簡単に見つかるよくあるタイプなので少し意外だったという感想、ゼロ埋めの問題は最初の記事ですでに議論されていたと思っていたこと、2つ目の最適化はもっと複雑で興味深かったが、やはり perf が導いた方向だったという強調、perf ツールの有用性を過小評価するなという助言
    • perf だけを使ったのではなく、C 版と Rust 版の差分プロファイリングと手動マッチングの過程を通じて把握したのだという正確な説明、perf diff 機能はあるもののシンボル名が異なるため自動マッチングが難しいという限界の指摘
    • aarch64 ベースの Apple デバイスの観点からアプローチしたという言及、異なる背景を持つ人なら、後から振り返れば「当然」に見える点でも素早く見抜けることがあるという経験談
  • 最近 ffmpeg の Twitter アカウントが Rust 関連イシューについて立場表明を行うことになった背景に今回の件があるのではないかという推測、該当ツイートへのリンク共有 https://x.com/ffmpeg/status/1924137645988356437?s=46
    • ffmpeg の Twitter アカウントを読んで ffmpeg の利用に懐疑的になったという率直な心境、代替がないのが残念だという思い、開発者コミュニティが非常に有害だという指摘、最大性能が重要な場合はあるが、外部とデータをやり取りする環境では ffmpeg に毎年複数回のリモート脆弱性(CVE)が発生し得ることへの言及、セキュリティ面で厳格なサンドボックスが必要だという強調、速くて安全なソリューションを共に作っていく中間地点が必要だという意見、関連リンク共有 https://ffmpeg.org/security.html
    • より良い反応は dav1d の性能改善で応じることだという提案、スポーツ記録にたとえて、記録を少し改善しただけでは実際の新記録達成に比べると感動が薄いという比喩、本当の解決策は実際に速く革新的な結果を出すことだという軽妙な説明