2020年4月21日火曜日

Rustで処理速度を改善: 実装言語を「Go」から「Rust」に変更、ゲーマー向けチャットアプリ「Discord」の課題とは

https://www.atmarkit.co.jp/ait/articles/2002/10/news038.html
シェアしました。

ゲーマー向けチャットアプリケーション「Discord」では、基盤サービスの一つである「Read States」が十分に高速化できない問題が明らかになった。開発チームは既存のコードをさらに改善することで対応しようとした。だが、Rust言語で再実装したところ、最適化を施す以前からパフォーマンスが向上した。なぜだろうか。開発チームがその理由を語る。2020年02月10日 11時40分 公開 Goで実装していたRead Statesサービスは、高速性が必要だという要件には十分対応できていなかった。平均すれば高速に動作していたものの、数分ごとに平均応答時間が急に大きくなり、ユーザーエクスペリエンスを損なっていた。調査したところ、これはGoの中核機能であるメモリモデルとガベージコレクタ(GC)に起因することが分かった。

 Discordで読み取り状態情報を保存するのに使うデータ構造を、以下では便宜的に「Read State」と呼ぶ。Discordには数十億のRead Stateがある。ユーザーとチャンネルの組み合わせごとに1つのRead Stateが必要だからだ。各Read Stateは幾つかのカウンターを持つ。例えば、カウンターの一つは、ユーザーがチャンネル内に持つメンション数を表す。
 これらのカウンターをバラバラに更新すると整合性が取れなくなる。アトミックに更新しなければならない。さらに頻繁に0にリセットしなければならない。
 アトミックカウンターを高速に更新するために、各Read Statesサーバには、Read StateのLRU(Least Recently Used)キャッシュがある。各キャッシュには数百万人のユーザー、数千万のRead Stateが含まれる。そして、キャッシュの更新頻度は毎秒数十万回に及ぶ。
 データの永続性を確保するために、Discordは分散データベース管理システム「Apache Cassandra」のクラスタでキャッシュを補完している。キャッシュキーのエビクション(不要なデータを追い出すこと)の際に、Read Stateをデータベースにコミットする。加えて、Read Stateの更新時に必ず、30秒間のデータベースコミットを入れている。こうしてデータベースへ毎秒数万回の書き込みが起こる。

Rustはなぜ高速なのか

 Rustではガベージコレクションが不要だ。同社がRead StatesサービスをRustで実装しようと考えたのはこれが理由だ。Rustを使えば、Goで実装した場合に生じた平均応答時間の急増は見られないだろう。
 Rustは、「メモリオーナーシップ」という考え方を取り入れた比較的ユニークなメモリ管理アプローチを採用している。メモリに所有権があるため、あるメモリ領域に対する読み書き権限を追跡でき、そのメモリ領域が不要になると、直ちに解放する。Rustはコンパイル時にメモリルールを強制するので、メモリバグはほぼ発生しない。Cのように手動でメモリ領域を追跡する必要はなく、コンパイラが全てを取り仕切る。

「非同期Rust」の完成度は低かったものの、コミュニティーの協力を得た

 Read Statesサービスを再実装した当時、Rustの安定版では非同期Rustのサポートが不十分だった。だが、ネットワークサービスでは、非同期プログラミングが必須だ。非同期Rustを有効にしたコミュニティー版のライブラリが幾つか存在したものの、利用に際して複雑な手順が必要であり、不具合が生じた場合のエラーメッセージは非常に分かりにくかった。
 幸いなことに、Rustチームは、非同期プログラミングが容易になるよう精力的に取り組んでおり、Rustのナイトリーチャネルで、非同期プログラミング機能が強化された不安定版が入手できるようになった。
 Discordはナイトリーリリースを導入し、問題が発生した際にはRustチームと協力して対処した。その結果、現時点では、Rustの安定版は非同期Rustをサポートしている。

0 コメント:

コメントを投稿