本番サービスを運用するには、内部で何が起きているかを把握する必要があります。しかし、メトリクスの追加は無料ではありません。InfoQが公開した講演で、IOP Systemsの共同創業者であるBrian Martinは、実装によっては、約5ナノ秒かかるカウンター更新が1マイクロ秒を超える処理になり得ると説明しています。ヒストグラムでは、スレッド間で競合が発生すると、差は約7ナノ秒から数十マイクロ秒にまで広がる可能性があります。
この講演の基本的な考え方は、特定のRustライブラリを選ぶことではなく、メトリクスをパフォーマンス設計の一部として扱うことです。毎秒何百万回も呼び出される経路に置かれたメトリクスは、小さなコストでも増幅します。一方、メトリクスがなければ、遅延や本番インシデントの診断、パフォーマンスの改善はより困難になります。
データの種類と更新コストを理解することから始める
Martinは、メトリクスを主に3種類に分類しています。通常は減少しない、リクエスト数などのカウンター。キューの深さなど、現在の値を表すゲージ。そして、レスポンスタイムなど、値の分布を表すヒストグラムです。この区別は重要です。種類ごとに必要な操作が異なり、ヒストグラムは単一の合計カウンターでは得られない情報を提供するためです。
最も単純なケースでは、整数カウンターにatomic fetch_addが適しています。一方、比較と交換、つまりCASのループでは、複数のスレッドが同じ場所を奪い合う際に、通常は再試行が必要です。コストの大部分は、コア間でのキャッシュラインの同期に起因します。講演では、32個の仮想CPUを備えたAWS Gravitonマシンでの測定結果が示されています。低コストなアトミック更新では理論上の処理上限が毎秒約1億1,900万リクエストだったのに対し、Prometheusを使った高コストな実装では約2,300万リクエストでした。
CPUごとの分割で競合を減らす
すべてのスレッドが1つのカウンターを共有すると、キャッシュラインがコア間を絶えず移動します。Martinはその代わりに、CPUごとに独立したカウンターを作る方法を提案しています。これにより書き込み操作はほぼ競合しなくなり、読み取り時に値を合算します。この方法では読み取りコストが少し増えますが、各リクエストで繰り返されるホットな書き込み経路を保護できます。
ここではfalse sharingに注意する必要があります。論理的に独立したカウンターでも、同じキャッシュラインに配置されていれば十分ではありません。キャッシュラインのサイズは64バイトであるため、64ビットのカウンター8個が隣接して配置される可能性があります。そのため講演では、カウンターをグループ化してパディングし、それぞれが別のキャッシュラインを占有するよう推奨しています。提示された数値によれば、適切な分割を使うことで、性能はアトミックカウンターの毎秒約1億1,900万リクエストから、毎秒約64億リクエストまで向上し得ます。
更新経路向けにヒストグラムを設計する
ヒストグラムのコストは、値が属するバケットを特定するところから始まります。バケットのリストを線形探索する方法は最も単純ですが最も遅く、二分探索では比較回数を減らせるものの、依然としてバケット数に左右されます。より高速な代替手段は、値を検索するのではなく、値からバケット番号を計算する直接インデックスです。
このインデックスにはトレードオフがあります。線形範囲への分割は高速ですが、小さい値では相対誤差が大きくなる可能性があります。対数インデックスは相対誤差をより適切に維持しますが、対数自体の計算コストが高くなります。Martinは、Log2に基づく外側の範囲と、精度を調整するサブバケットを組み合わせる方法を紹介しています。これはHDR HistogramやH2Histogramで使われている方式です。非アトミックなテストでは、バケットの特定と更新にHDR Histogramで約2.65ナノ秒、H2Histogramで約2.15ナノ秒かかりました。
近似的な一貫性はいつ許容できるのか?
ヒストグラムのコストはインデックス方式だけで決まりません。アプリケーションによっては、1回の操作で複数のアトミック操作を更新したり、値の合計にCASを使ったり、整合したスナップショットを得るためにロックを使ったりします。講演では、一部の実装が2マイクロ秒を超え、32コアで数十マイクロ秒に達した一方、直接インデックスと1回のアトミック更新に基づく実装は、カウンターに近いコストだったと示されています。
代替案は近似的な一貫性です。ヒストグラムの読み取り中に一部のバケットが変化する可能性はありますが、メトリクス自体がもともと近似的なものである場合、連続する2回の読み取りの差は依然として有用です。これは一般則ではありません。完全に整合したスナップショットを必要とするシステムでは、コストがかかっても同期を選ぶ場合があります。
certi.newsによる編集部の見解
実務上変わるのは、メトリクスを追加する判断に、メトリクス名だけでなく更新の構造も含める必要があるという点です。アトミックカウンター、CPUごとの分割、直接インデックスを使えば、センシティブな経路の内部でも計測を利用できる可能性があります。一方、同期型または動的に拡張可能なヒストグラムでは、高負荷時に大きなコストが発生する可能性があります。この講演は、すべてのライブラリやサービスに適用できる単一のレシピを提示しているわけではありません。柔軟性、他のプロジェクトでライブラリを利用できること、一貫性、パフォーマンスは、部分的に相反する目標であることを示しています。そのため、ライブラリ名や非競合時の結果に頼るのではなく、対象とする競合レベルと処理量の下で実際の実装をテストすべきです。
Martinはまた、Rezolusプロジェクトを通じてeBPFを使用し、カーネルコードを変更せずに、スケジューラー、システムコール経路、TCPスタックなど、Linuxカーネルから詳細なメトリクスを取得する方法も紹介しています。残る未解決の問題は、各ケースでどの程度の精度と一貫性が必要なのか、そしてシステムを拡張した際にも読み取りや断片の集約コストが許容範囲に収まるかどうかです。
ニュースの出典
InfoQ - Architecture Articles
原文を開く ↗