- recallは数百社に会議用ボットを提供するサービスで、AWS上で大規模なインフラを運用している
- コスト効率の高いサービスのために、ハードウェア性能を最大限活用しようとしていた
- 過去数年間、クラウドプロバイダーのGPU可用性が不安定だったため、動画処理はGPUではなくCPUで実行していた
- ヘッドレスChromiumを使うボットをプロファイリングした結果、CPU時間の大半は動画処理(エンコード/デコード)ではなく、メモリコピー関数
__memmove_avx_unaligned_erms と __memcpy_avx_unaligned_erms に費やされていた
memmove と memcpy はC標準ライブラリ(glibc)にあるメモリブロックコピー関数
memmove は重複する範囲のメモリコピーに関するいくつかの例外ケースを処理するが、両方とも「メモリコピー」関数に分類できる
avx_unaligned_erms という接尾辞は、Advanced Vector Extensions(AVX)をサポートするシステム向けに最適化され、未整列メモリアクセスにも最適化されていることを意味する
erms は Enhanced REP MOVSB/STOSB の略で、最新のIntelプロセッサで高速なメモリ移動を行うための最適化。「特定プロセッサ向けのより高速な実装」と考えられる
- プロファイリングの結果、これらの関数を最も多く呼び出していたのはデータを受信するPython WebSocketクライアントだった
- その次に多かったのは、データを送信するChromiumのWebSocket実装だった
WebSocketの問題点
- 生の動画データをChromiumのJS環境からエンコーダへ送るために、ローカルWebSocketサーバーを使っていた
- 生の1080p 30fps動画ストリームは、毎秒93MBを超える高い帯域幅を要求する
- WebSocketの利用が大きな計算コストを招いており、主な原因は断片化(fragmentation)とマスキング(masking)だった
- 断片化: ChromiumのWebSocket実装は131KBを超えるメッセージを複数フレームに断片化する。3MBを超える生の動画フレームは24個以上の個別フレームに分割されて送信される
- マスキング: セキュリティ上の理由から、WebSocketはクライアントからサーバーへ送信されるすべてのフレームをマスクする。毎秒100MBを超える大容量データでは無視できないオーバーヘッドになる
代替案の模索
- ブラウザAPIだけではWebSocketよりはるかに高性能なものを実装するのが難しかったため、Chromiumをフォークしてカスタム機能を実装することにした
- 3つの代替案を検討した: raw TCP/IP、Unix Domain Socket、Shared Memory
- TCP/IP: WebSocketの断片化/マスキング問題は回避できるが、最大パケットサイズが小さいため依然として断片化の問題がある。カーネル空間へのコピーオーバーヘッドもある
- Unix Domain Socket: ネットワークスタックを完全に迂回できるが、ユーザー空間とカーネル空間の間でデータコピーが必要
- Shared Memory: 複数プロセスが同時にアクセスできるメモリ。途中でコピーせずにChromiumが共有メモリへ直接書き込み、エンコーダがそのまま読み取れる
共有メモリベースの転送実装
- 共有メモリにデータを連続して読み書きするため、リングバッファ形式で実装した
- 要件: lock-free、マルチプロデューサ/シングルコンシューマ、可変フレームサイズ、ゼロコピー読み取り、サンドボックス親和性、低遅延シグナリング
- 既存のリングバッファ実装を評価したが、要件をすべて満たすものがなかったため自前で実装することにした
- ゼロコピー読み取りをサポートするため、ポインタを write、peek、read の3つに分けた
- スレッド安全性のためにアトミック演算を使い、新しいデータの発生や空き領域の確保を通知するために named semaphore を使用した
- 共有メモリベースのリングバッファ実装とその他の最適化により、ボットのCPU使用量を最大50%削減できた。結果としてAWS費用を年間100万ドル以上削減した。
3件のコメント
Hacker Newsの意見
あるスタートアップが「十分に良い」近道を選び、後で最適化するという典型的な話だという意見。
生の動画データの帯域幅の大きさに驚いたという意見がある。
AWS の問題ではなく、CPU サイクルを浪費した問題だという意見がある。
TCP/IP ネットワークの MTU と MSS が動画フレームサイズに比べて小さい点を指摘している。
Chromium の Mojo を使えば、プラットフォームごとのコードを気にする必要がないという意見がある。
問題はネットワークではなく、動画コーデックへの理解不足だという意見がある。
透明性を称賛し、製品価格についての透明性も望むとしている。
WebSocket プロトコルのマスキングは中間者の問題を解決しようとする試みだと説明している。
動画データを圧縮せずに転送する方式はおかしいと指摘している。
生の動画を WebSocket で送る初期アプローチに驚いたとしている。
最初から開発を間違えていたんだな…
「生の動画をWebSocketで送信するという初期アプローチは驚きだ」とのこと。これには共感する。