世界を動かす技術を、日本語で。

FFmpeg開発者が手書きのアセンブリコードのおかげでさらに100倍の飛躍を誇る

概要

  • FFmpegプロジェクト が手書きアセンブリで大幅な性能向上を実現
  • 最新パッチで特定関数にて 100倍の高速化
  • 性能向上は 特定のフィルター(rangedetect8_avx512) に限定
  • AVX512非対応CPUでも 64%の速度向上
  • 手書きアセンブリの有効性と現代コンパイラの限界を示唆

FFmpeg、手書きアセンブリで100倍高速化

  • FFmpeg開発者 が手書きアセンブリによる 大幅なパフォーマンス向上 を発表
  • 最新パッチ適用で、 rangedetect8_avx512関数100倍高速化
  • この速度向上は FFmpeg全体 ではなく、「 単一関数」への適用であると開発者が強調
  • AVX512に非対応なCPU環境でも、 rangedetect8_avx2コードパス64%の速度向上
  • 性能向上が体感できるのは、 一部の特殊なフィルター利用時 に限定

高速化の背景と技術的詳細

  • 高速化された関数は 「rangedetect8_avx512」 という 比較的マイナーなフィルター
  • このフィルターはこれまで優先度が低く、最適化の対象外だった経緯
  • SIMD( Single Instruction, Multiple Data)の活用で 並列処理性能 を最大化
  • 現代の コンパイラによる最適化 では到達できないレベルのパフォーマンス
    • 開発者は「 register allocator sucks on compilers」と指摘
  • 手書きアセンブリは 1980~90年代のホームコンピュータ時代 を彷彿とさせる技術
    • 当時は 限られたリソース を最大限活用するために必須の手法

FFmpegと関連プロジェクト

  • FFmpeg はLinux、Mac OS X、Microsoft Windows、BSD、Solarisなど 多様なOSで動作
  • VLC などの人気メディアプレイヤーも FFmpegのlibavcodecやlibavformat を利用
  • FFmpeg開発チーム はアセンブリの最適化手法を教える「 school」も運営
  • 現在も数少ない「 アセンブリエバンジェリスト」としての存在感を発揮

今後の展望・まとめ

  • 今回の 100倍高速化 はごく一部の関数に限定される
  • それでも 手書きアセンブリの有効性現代コンパイラの課題 を再認識
  • 今後も 特定用途での最適化 に期待
  • 高度な最適化技術の継承と発展がFFmpegの強み

Hackerたちの意見

まだPipewireとxdgデスクトップポータルの画面/ウィンドウキャプチャサポートがffmpeg CLIに来るのを待ってる。ずっと足踏みしてる感じだね。

記事によっては100倍って言ったり、他のところでは100%のスピードブーストって言ったりしてる。例えば、「アプリの‘rangedetect8_avx512’のパフォーマンスを100.73%向上させる」って言ってるけど、スクリーンショットでは100.73倍って表示されてる。100倍だと9900%のスピードブーストになるし、100%のスピードブーストだと2倍速ってことになる。どっちなんだ?

ffmpegの人たちは100倍って言ってるみたいだね、100%じゃなくて。記事には多分誤植があるんじゃないかな。

単一の機能に対しては100倍、フィルター全体に対しては100%(2倍)ってことだね。

名前から判断すると、その関数は8ビットの値で動作してるんじゃないかな。もし前の実装がスカラーだったら、ダブルポンプのAVX512実装で128要素を同時に処理できるから、100倍のスピードアップはあり得るね。

スクリーンショットにある通り、確かに100倍(または100.73倍)だね。9973%のスピードアップを表してるけど、記事の中でパーセンテージの表記が間違ってるところがあるよ。

x86/x86-64アーキテクチャ専用(AVX2とAVX512)。10年以上もみんながx86を使ってたのに、理論的にはSIMD最適化が広範囲に及ぶはずだったのに、拡張アーキテクチャはかなりひどかった(新しいものが使えるとは限らなかった)。で、やっと新しくて良いx86 SIMDが使えるようになったのに、x86の普及に依存できなくなったのは皮肉だね。

AVX512は一連の拡張機能だよ。使いたいAVX512命令をすべて実装しているAVX512 CPUに頼ることはできないし、基本命令に絞らないとね。最近のエンコーダはスレッド間でのスケーリングも良くなってるけど、無限ではない。数年前に組み込みプロジェクトにいた時、SoCのビデオエンコーダを安定して動かすためにかなりの時間をかけたけど、誰かがffmpegを実行して、複数のCPUコアを使えばより良い結果が得られることに気づいたんだ。

HEVCのためにSIMD最適化を10年やってた時、アセンブリ版と普通のCを比べるのはちょっとしたジョークみたいだった。だって、100倍みたいなありえない倍率が出てくるから。実際には、最初から非常に非効率だったってことを意味してる。細かいところが肝心で、マイクロベンチマークは通常、同じ関数をループで百万回呼び出して、キャッシュが効いてオーバーヘッドがCPUサイクルだけになるけど、実際にはそんな使われ方はしない。たくさんの他の処理の中で一度呼ばれるかもしれない。少なくともキャッシュが熱くならないように大きなテスト領域を作ることはできるけど、彼らがそれをやるとは思えないな。

ちょっと脱線しちゃってごめんけど、SIMDの経験がめっちゃあるみたいだね。ISPC使ったことある?それについてどう思う?今の時代に手動でSIMDコードを書くのはちょっとおかしいと思う。普通のコンパイラは自動ベクトル化が下手だし、GPUカーネルではそんなことなかったのに。

ffmpegはマイクロベンチマークとそんなに変わらないよ。プログラム全体は基本的にこうだね:while (read(buf)) write(transform(buf))

それが本当に意味するのは、最初からすごく非効率だったってことだよ。僕は根本的な意味よりも結果の方が大事だから、それは当たり前だと思ってる。

なんかSound Open Firmware (SOF)を思い出すな。最適化されてないgccか、Xtensa HiFi SIMDの命令を使えるCadence XCCコンパイラでコンパイルできるんだ。 https://thesofproject.github.io/latest/introduction/index.ht...

実際、最適化されたCよりアセンブリの方が速いって聞いてちょっと驚いた。今のコンパイラはすごく良くなってるから、手書きのアセンブリから得られるメリットは微々たるものだと思ってた。どうやら間違ってたみたいだね。いつかちゃんとアセンブリを学ぶべきかな…

コンパイラは、処理しなきゃいけないゴミの量を考えるとすごく優秀だけど、プログラムの使われ方についての情報がゼロ(デフォルトで)だから、彼らを超えるのはそんなに難しくないんだよね。

リンク先のパッチを見ると、ベースライン(ff_detect_range_c)[1]は普通のスカラーCコードで、スピードアップは同じ計算のAVX-512バージョン(ff_detect_rangeb_avx512)[2]で達成されてるのがわかる。FFmpegの開発者たちは、自分たちが管理しているベクトル幅に依存しないマクロのライブラリを使って、アセンブリを直接書くのを好むけど、ざっと見た感じ、同等のコードはIntelの命令を使えばCで簡単に表現できそうだね(それが好きなら)。まあ、実質的にはアセンブリと同じだけど、レジスタアロケータがあるから実際の違いは限られてる。スピードアップの大部分はベクトル化によるもので、アセンブリではない。大まかに言うと、現代のコンパイラは最も単純なループ(例えばドット積)を超えてベクトル化できないし、そもそもそれをお願いしないといけない(例えばgcc -O3、他の場合ではしばしば-O2より遅いこともある)。だから、こういう数学的なコードでは、広いベクトル(AVX/AVX2やAVX-512)と比べてパフォーマンスが数十倍遅れることもあるよ、特に個々の要素が小さい場合(ここでの8ビットみたいに)。現代のスーパースカラーCPUでの非常にタイトなスカラーコード…時にはコンパイラを意味のある差で上回ることもある(今の例だと40%のスピードアップ)。でも、すごく注意が必要だね(依存関係や実行ポートの負荷を考えて)、そのチャンスはそんなに多くないし(そもそもスカラーコードを書く理由は何なの?)。 [1] https://ffmpeg.org/pipermail/ffmpeg-devel/2025-July/346725.h... [2] https://ffmpeg.org/pipermail/ffmpeg-devel/2025-July/346726.h...

効率を上げるのはAVX512であって、アセンブリじゃないよ。このカーネルはシンプルすぎて、AVX512のインライン関数を使ったCよりも速くならないと思う。100倍の差があるのは、a) min/maxがSIMDでは単一命令なのに対して、スカラーではcmp+cmovだから、b) u8精度で動作しているから、各AVX512命令が64倍のmin/maxを処理できるからなんだ。だから、スカラーの最適化されていないものは1バイト未満のサイクルあたりのスループットだけど、AVX512版はL1とL2の帯域幅をフルに活用できるよ。(Zen 5ではサイクルあたり128Bと64B。)でも、このカーネルはフレーム全体を処理してるから、もしメガピクセルを超えるとL3に行かなきゃならないなら、効果は半分になるはず(CPUによるけど、Zen 5を仮定して)、フレームがL3に常駐していないとさらに効果が減るね。

もし低レベルの最適化にもっと関わることがあったら、Cコンパイラが頭を抱える最初の瞬間を1時間も経たずに見つけることになるよ。ランダムな例として: https://stackoverflow.com/questions/71343461/how-does-gcc-no... 問題のコードは何兆回も呼ばれたから、実際に重要だったんだ。

パフォーマンスが重要なc/c++ライブラリのほとんど(strlenのように一見平凡なものも含めて)は、専門的に手書きされたアセンブリを使ってるよ。コンパイラはほとんどの人にとって十分良いけど、これはほとんどの人がこのレベルで最適化する価値のあるソフトウェアを書いていないからなんだ。

SIMDインライン関数に落とし込むことでコンパイラに勝つのはすごく簡単だよ。最近、4部構成のガイドを書いたんだけど… https://scallywag.software/vim/blog/simd-perlin-noise-i

具体的にはSIMDだよ。SIMDはめちゃくちゃ速いし、コンパイラはまだそれを生成するのが得意じゃないんだ。

記事は実際に何が影響を受けるのかが不明瞭だね。「rangedetect8_avx512」について触れてるけど、それを「マイナーな関数」って呼んでる。実際にはどんな状況で使われるの?全体の変換プロセスでのリアルタイムのパフォーマンス向上はどれくらい?

これは変換じゃないよ。むしろ、このフィルターは、ピクセルがビデオかフルレンジか、アルファがプレマルチプライされているかどうかわからないビデオに使われて、その情報を判断するためのものなんだ。通常はメタデータで正しくタグ付けするためにね。問題の関数は特に色範囲の部分に関してなんだ。

昔々、ビデオはアナログ信号だったんだよね。波がぐにゃぐにゃしてて、オシロスコープで見ることができたんだ。一つのやり方として、制御情報をバンド内にエンコードしてた。これは、テキストファイルに編集者のメモを入れるような感じかな。特に、次の行に行く時や次のフレームに行く時を知らせるために、真っ黒よりも黒い色を使ってたんだ。後にDVDが登場したけど、DVDはデジタルだった。でも、最終的にはアナログケーブルを通してアナログテレビに送られるデータをエンコードする必要があったから、DVDは255の中で16よりも暗い色を使って、真っ黒よりも黒いことを示してたんだ。このデジタル信号はアナログ信号にデコードされて、そのままケーブルに送られたんだ。だから、DVDは一見8ビットのカラーだけど、実際には1チャンネルあたり7.9ビットくらいなんだよね。これはBluRayやHDMIにも当てはまる。最近では、その0.1ビットを取り戻したいってことになって、いくつかのコーデックは0-255のフルレンジをバンド内信号としてエンコードするようになったんだ。でも、問題は、時々人がコーデックに信号の範囲が0-255なのか16-255なのかをちゃんと伝えられないことなんだよね。これが本当に違いを生むんだ。時には、番組や映画を見てると、暗い部分がめちゃくちゃになってることがある。これが起こる理由はいくつかあって、その一つが黒レベルが間違ってることなんだ。この機能は、すべてのピクセルが16-255か0-255の範囲にあるかをスキャンして判断してるみたい。もしコーデックがピクセル値が16-255であることを確信できれば、エンコード時にビットを節約できるんだ。でも、私が間違ってるかもしれない。私は日中ビデオの仕事をしてるけど、恥ずかしいことに、黒レベルを正しく扱えてないんだよね。

関連情報: ffmpegのアセンブリを書くためのガイド: https://news.ycombinator.com/item?id=43140614