科学・技術
平均値は意味がない:レイテンシ問題をデバッグするためのデータ可視化
The mean means nothing: data visualization to debug a latency problem (fzakaria.com)
要約
この記事では、平均値だけでは見落としがちなレイテンシ問題のデバッグ方法について解説しています。筆者は、平均レイテンシが上昇したように見えても、実際には中央値が改善し、一部のテールエンド(p95, p99)が大幅に悪化しているケースを例に挙げ、累積分布関数(CDF)やリッヂライン、ヒートマップなどの可視化手法が、データの全体像を把握し、問題の根本原因(例:キャッシュヒットとミス)を特定する上でいかに有効であるかを説明しています。
全文翻訳
最近、$DAYJOB で lld に関するパフォーマンス改善を検証していたのですが、ベンチマークでは改善が見られたものの、ライブプロダクションのダッシュボードではそれが確認できないという、少しフラストレーションのたまる状況にありました。
ウェブサービスで働いた経験から、個々の時系列ダッシュボード、時には数パーセンタイルを見ることに慣れており、目に見える変化を期待していましたが、データは結論を出すにはノイズが多すぎました。
偶然、同僚もビルド速度の改善を評価しようとした際に同様の問題に直面していたことがわかりました。
ビルドに影響を与える可能性のある変数は数多くあります(コールドキャッシュ、インクリメンタル、ローカル、リモートなど)。システムの状態やワークロードによっては、ビルド時間は大きく変動する可能性があります。
彼女はデータの可視化に累積分布関数(CDF)を活用し、それが私にとって啓示となりました。
これにより、CDF に加えて、データの可視化のいくつかの異なる方法を探求することになり、単一の画像や統計量では全体像を伝えるには不十分であることが多いことを学びました。
この記事では、単一の合成データセットを例に、異なる可視化が同じデータについてどのように異なるストーリーを語るかを示します。
目標は、データを要約する単一の数値だけでなく、データを見るように説得することです。
以下のすべては、固定シードを持つ単一の合成データセットから来ています。
完全なスクリプトはこのgistで見つけることができます。
それは nix-shell シェバンを持つ単一のファイルなので、nix を使用している限り、すべての図を正確に再現できます。
注意
この記事のデータとチャートの生成には AI を活用しました。それが気になる場合は、すみません。
「悪化させた」ロールアウト
設定は次のとおりです。
私たちは典型的なウェブサービスを運営しており、リクエストレイテンシを削減することを期待して、1週間にわたって新しいキャッシュ層をロールアウトしました。
変更は完全にデプロイされ、平均値をプロットするレイテンシダッシュボードは次のようになります。
平均レイテンシは 112 ms から 122 ms に上昇しました。
☹️
SEV がカットされ、変更を元に戻し、ポストモーテムを作成します。右?
🤔
1つの数字、4つのストーリー
特にウェブサービスでは、さまざまなパーセンタイル、特に分布のテールエンド(p95 および p99)を見ることが良い習慣であることがよくあります。
統計量 | 変更前 | 変更後 | 変化
------- | -------- | -------- | --------
平均値 | 112 ms | 122 ms | +9%
p50 (中央値) | 99 ms | 54 ms | −46%
p95 | 224 ms | 454 ms | +103%
p99 | 309 ms | 678 ms | +119%
これで問題が発生しました。そして問題は、誰もが正しいということです。
平均値は、変更が軽微な後退であることを示しています。
中央値(p50)は、変更が大きな勝利であり、典型的なリクエストがほぼ 2 倍速くなったことを示しています。
p99 は、SEV11SEV、「Severity」は、インシデントまたはアウトエージを説明するために普及した方法です。SEV は、影響の降順で数値的にランク付けされることがよくあります。例えば、SEV0 が最も深刻です。 、最悪のリクエストは 2 倍以上になりました。
同じ数値から計算された平均値と中央値は、反対の方向を指しています。
エンジニアはデータ指向であるように教えられることが多いですが、議論を裏付ける統計量を cherry-pick するのは簡単です。
形状を見る
分布の次の基本的な操作は、その形状をプロットすることです。
ここでは、密度としての 2 つのレイテンシ分布(変更前と変更後)を示します。
そこにあります。
🤓☝️
「変更前」は 1 つの整然としたこぶです。
「変更後」は 2 つのこぶです。
これはすでに以前の矛盾を説明していますが、正しく視覚化するのは少し難しいです。
形状は選択した平滑化パラメータに依存し、 2 つの塗りつぶしは重なり合う場所で互いに不明瞭になり、パーセンタイルを読み取るのは実際に困難です。
2 つの母集団があることはわかります。中央値がどこに行ったかは簡単にはわかりません。
あなたが使用していない最高のチャート
累積分布関数(CDF)は、すべてのパーセンタイルに対して 1 つの質問に答えます。
x ミリ秒以下で着信したリクエストの割合はどれくらいですか?
CDF は、複数のパーセンタイルを 1 つのチャートに視覚化するための非常に簡単な方法です。
カーブに応じて、リクエストレイテンシが母集団全体にどのように分布しているかを理解できます。
変更前と変更後の CDF を同じチャートにプロットして、それらを比較すると非常に役立つことがわかりました。
さまざまなパーセンタイルでのシフトを視覚化し、変更が母集団全体にどのように影響したかを理解できます。
私たちのストーリーでは、「変更後」の曲線は 140 ms 未満のリクエストレイテンシで左にシフトしています。
これは、以前よりも速く完了するリクエストが増えていることを意味します。
「変更後」の曲線は、140 ms より右側で「変更前」の曲線よりも高くなっています。
これは、より遅く完了するリクエストが増えていることを意味します。
2 つの曲線は約 140 ms で交差します。
これは、変更が勝利から損失に変わるティッピングポイントです。
ヒント
2 つの CDF が交差することは、単一のパーセンタイルでは要約できない変更の明白な兆候です。
なぜなら、効果の符号は、どのパーセンタイルに尋ねるかによって異なるからです。
誰が勝ち、どれだけ勝ったか
CDF は、効果の符号が変わることを示しています。
速いか遅いか。
次の明白な質問は、分布の各点での影響の大きさです。
各パーセンタイル p について、変更後のレイテンシから変更前のレイテンシを引いたもの(シフト関数として知られる)をプロットできます。
ゼロ線の下では、変更は速くなります。
その上では、遅くなります。
各パーセンタイルでの変更の大きさを視覚化できます。
回帰は最初からそこにありました
これまでは、変更前と変更後の 2 つの静止したスナップショットを見てきました。
しかし、ロールアウトはしばしば即時ではありません。
このストーリーでは、 0% から 100% のトラフィックに徐々に移行しながら、 1 週間かけて新しいキャッシュ層をロールアウトしました。
各日はどのようなものでしたか?
1 日あたりの分布を積み重ねると、リッヂラインが得られます。
ロールアウトが進むにつれて、メインのピーク(速いリクエスト)が左にスライドし、 2 番目のピーク(遅いリクエスト)が右に出現するのを見ることができます。
中央値は低下していますが、遅いリクエストは静かに数とレイテンシが増加しています。
ここの x 軸は対数です。
レイテンシはおおよそ対数正規分布であり、線形軸では速いピークは高いスパイクの隣に目に見えない広がりがありますが、対数軸により両方のこぶがこぶとして読み取ることができます。
同様のことを、 1 つのグリッドに圧縮して、ヒートマップとして行うことができます。
1 日あたり 1 列、各レイテンシで着信するトラフィックの量を示す色です。
新しい母集団がかすかに現れているのをぼんやりと見ることができます。
1 週間全体で計算された集計値は、これら 7 つの非常に異なる日を 1 つの不明瞭な数値にブレンドし、傾向を完全に隠していたでしょう。
二峰性は原因があった
これで何が起こったのかを徹底的に確立しました。
次の質問はなぜかです。
これは実際、$DAYJOB でレイテンシ分布の二峰性を観察するためにデータをバイナリサイズ(例:>50MiB)でカットする必要があったのと非常によく似ています。
私たちのストーリーでは、新しいレイヤーはリクエストをキャッシュから提供するか(ヒット)、追加のホップでバックエンドにフォールスルーするか(ミス)のいずれかです。
「変更後」のリクエストをそのプロパティで分割し、それぞれに CDF を描画できます。
キャッシュの結果に応じて、各母集団は再び単峰性になります。
キャッシュヒットは左にシフトしているため、ベースラインよりも高速であることが簡単にわかります。
キャッシュミスは追加のホップの代償を払い、はるかに右側に着地します。
なぜそれらのリクエストなのか?
「一部のリクエストはキャッシュをミスする」はメカニズムですが、まだ原因ではありません。
どのリクエストがミスし、なぜミスするのか?
各リクエストには、まだ使用していないもう 1 つのフィールドがあります。
それはレスポンスサイズです。
キャッシュは小さく、ホットなオブジェクトを保持します。
大きなものは削除されるか、まったく収まりません。
レイテンシとレスポンスサイズをプロットし、各点をキャッシュヒットかミスかで色分けできます。
また、両方の母集団が各軸にどのように分布しているかを確認するために、各マージンに密度を追加することもできます。
ジョイントプロットです。
2 つのクリーンな母集団クラスター(小さい&速い(キャッシュヒット)と大きい&遅い(キャッシュミス))を見ることができます。
レイテンシ分布の二峰性は、レスポンスサイズ分布の二峰性によって引き起こされていることは明らかです。
これで、アクション可能なものができました。
キャッシュの最大オブジェクトサイズを増やすか、大きなレスポンスを分割します。
🔥
グラフは 1000 の数字の価値がある
多くの場合、単一のパネルまたはグラフは、最良の場合でも全体像を伝えるには小さすぎます。
最悪の場合、誤解を招く可能性があります。
同じデータの複数のビューを持つことは有益です。