TT Lab
はじめる
学ぶ 学習パス コース

負荷テスト

性能回帰だと言って戻した変更は、実はノイズだった

TT Labで続きを見る

目標

同じ対象を5回繰り返して、ノイズの大きさを先に測り、レイテンシを少し増やしたバージョンと、大きく増やしたバージョンを、それぞれ5回ずつ測って、どちらがノイズを超えているかを、ルールで判定します。そのルールを、ベースラインのディレクトリと新しい測定のディレクトリを受け取って終了コードを出す、CIゲートのスクリプトにし、1回だけの比較が、同じコードも回帰にしてしまうことを、数字で確認します。

なぜ重要なのか

性能の回帰の判定は、統計の問題ではなく、運用の問題です。しきい値を誤ると、どちらかが起きます。誤検知が頻繁で、人がゲートを切るか、しきい値が緩くて、本物の回帰が通り過ぎるか。どちらの場合も、ゲートがないのと同じになります。そのため、しきい値は、会議室で決めたパーセンテージではなく、そのとき、そのマシンで実際に測ったノイズである必要があります。ノイズを測るには、何も変えないまま、同じテストを何回も回してみる必要があり、そうすると、自然に「何回回すか」が、時間と感度のトレードオフであることが現れます。1回しか回さなければ、しきい値が0になって何でも回帰になり、多く回すほど、より小さな回帰を自信をもって見つけられます。この判断を、人の目に任せず、スクリプトの終了コードとして残すことが、このラボの最後のステップです。

ステップ

  1. /opt/lab/lt/lt-regression/svc.pyを、VERSION=base DELAY_MS=20 JITTER_MS=20 SEED=1234で127.0.0.1:8080に起動してください。hey -n 200 -c 5 -o csv http://127.0.0.1:8080/workを5回回して、出力を/root/lt-regression/base/run1.csvからrun5.csvまで保存します。実行ごとにp50を求めて、/root/lt-regression/01-noise.tsvに5行で書いてください。各行はタブで区切った2列run<번호> <p50 밀리초>で(プレースホルダーは、番号とp50のミリ秒です)、ミリ秒は小数第3位までです。p50は、csvの最初の列(response-time、秒単位)をミリ秒に変えて昇順にソートしたあと、int(n/2)+1番目(1から数えて)の値で決めます。そして/root/lt-regression/01-spread.txtに、4行min=・max=・spread_ms=<max - min>・spread_pct=<(max - min) ÷ min × 100, 소수 첫째 자리>を書きます(プレースホルダーは小数第1位です)。
  2. ステップ1の5回の実行のうち、p50が最も小さい実行と、最も大きい実行を選んでください。/root/lt-regression/02-illusion.txtに、6行を書きます。fastest_run=<번호>・slowest_run=<번호>・fastest_p50=<밀리초>・slowest_p50=<밀리초>・apparent_change_pct=<(느린 값 - 빠른 값) ÷ 빠른 값 × 100, 소수 첫째 자리>・same_code=yesです(プレースホルダーは、番号、ミリ秒、遅い値と速い値を使った式で小数第1位です)。ミリ秒は小数第3位まで書きます。この2つは、同じバージョンを同じ条件で測ったものです。
  3. 同じ/opt/lab/lt/lt-regression/svc.pyを、VERSION=small DELAY_MS=21 JITTER_MS=20 SEED=1234で127.0.0.1:8081に追加で起動してください(8080はそのままにします)。ステップ1とまったく同じ負荷を5回かけて、/root/lt-regression/small/run1.csvからrun5.csvまで保存し、/root/lt-regression/03-small.tsvに、ステップ1と同じ形で5行を書いてください。そして/root/lt-regression/03-median.txtに、median_of_medians=<다섯 p50 의 중앙값, 소수 셋째 자리>の1行を書きます(プレースホルダーは、5つのp50の中央値で小数第3位です)。中央値の定義は、ステップ1と同じです。
  4. /opt/lab/lt/lt-regression/svc.pyを、VERSION=big DELAY_MS=30 JITTER_MS=20 SEED=1234で127.0.0.1:8082に追加で起動し、同じ負荷を5回かけて、/root/lt-regression/big/run1.csvからrun5.csvまで保存してください。/root/lt-regression/04-big.tsvに5行、/root/lt-regression/04-median.txtにmedian_of_medians=の1行を、ステップ3と同じ形で書きます。
  5. /root/lt-regression/05-rule.txtに、4行を書いてください。metric=p50・stat=median_of_5・noise_ms=<1단계 다섯 실행의 최대 - 최소, 소수 셋째 자리>・rule=<이 규칙을 한 문장으로, 40자 이상>です(プレースホルダーは、ステップ1の5回の実行の最大−最小で小数第3位、このルールを1文で40文字以上です)。そして/root/lt-regression/05-verdict.tsvに、3行を書きます。各行はタブで区切った3列<이름> <delta_ms> <judgment>で(プレースホルダーは名前です)、名前はbase・small・bigの順です。deltaは、そのバージョンの5回の実行の中央値から、baseの5回の実行の中央値を引いた値(小数第3位)で、judgmentは、deltaがnoise_msより大きいときregression、そうでなければnoiseです。
  6. /root/lt-regression/gate.shを作成してください。bash gate.sh <기준선 디렉터리> <새 측정 디렉터리>で呼び出すと(プレースホルダーは、ベースラインのディレクトリと、新しい測定のディレクトリです)、2つのディレクトリの*.csvをそれぞれ読んで、実行ごとにp50を求め、各グループの中央値を出します。出力は1行で、delta=<밀리초> noise=<밀리초> REGRESSIONまたはdelta=<밀리초> noise=<밀리초> OKです(プレースホルダーはミリ秒です)。deltaは、新しい測定の中央値からベースラインの中央値を引いた値、noiseは、ベースラインの実行のp50の最大から最小を引いた値で、どちらも小数第3位までです。deltaがnoiseより大きければ終了コード1、そうでなければ0で終了します。採点ツールは、自分で作った3セットの入力でこのスクリプトを回して、合格と失敗の両方を確認します。
  7. ステップ1の5回の実行のうち、p50が最も小さいものを/root/lt-regression/one-base/run.csvに、最も大きいものを/root/lt-regression/one-new/run.csvにコピーしてください(2つのファイルとも、baseバージョンの測定です)。その2つのディレクトリでゲートを回して、出力を/root/lt-regression/07-gate-out.txtに保存し、/root/lt-regression/07-onerun.txtに、5行を書きます。noise_ms=<게이트가 낸 값>・delta_ms=<게이트가 낸 값>・gate_exit=<종료 코드>・false_positive=<yes 또는 no>・min_runs=<기준선을 최소 몇 번 돌려야 한다고 볼 것인가, 2 이상의 정수>です(プレースホルダーは、ゲートが出した値、終了コード、yesまたはno、ベースラインを最低何回回す必要があると見るかで2以上の整数です)。
  8. ゲートをさらに2回回してください。bash gate.sh base bigの出力は/root/lt-regression/08-gate-big.txtに、bash gate.sh base baseの出力は/root/lt-regression/08-gate-same.txtに保存します。そして/root/lt-regression/08-decision.txtに、6行を書いてください。big_exit=・same_exit=・rollback=<yes 또는 no, big 판을 되돌릴 것인가>・min_runs=<5 이상의 정수>・threshold_rule=<문턱을 어떻게 정할 것인가, 40자 이상>・why=<60자 이상>です(プレースホルダーは、yesまたはnoでbigバージョンを元に戻すか、5以上の整数、しきい値をどう決めるかで40文字以上、60文字以上です)。

参考

何も変えずに5回測って、ノイズを求める

/opt/lab/lt/lt-regression/svc.pyを、VERSION=base DELAY_MS=20 JITTER_MS=20 SEED=1234で127.0.0.1:8080に起動してください。hey -n 200 -c 5 -o csv http://127.0.0.1:8080/workを5回回して、出力を/root/lt-regression/base/run1.csvからrun5.csvまで保存します。実行ごとにp50を求めて、/root/lt-regression/01-noise.tsvに5行で書いてください。各行はタブで区切った2列run<번호> <p50 밀리초>で(プレースホルダーは、番号とp50のミリ秒です)、ミリ秒は小数第3位までです。p50は、csvの最初の列(response-time、秒単位)をミリ秒に変えて昇順にソートしたあと、int(n/2)+1番目(1から数えて)の値で決めます。そして/root/lt-regression/01-spread.txtに、4行min=・max=・spread_ms=<max - min>・spread_pct=<(max - min) ÷ min × 100, 소수 첫째 자리>を書きます(プレースホルダーは小数第1位です)。

csvの1行目はヘッダーなので、飛ばす必要があります。gawkのasortでソートすれば、1行で済みます。この対象はCPUを使わずに眠るだけなので、ここで見える揺れは、マシンの競合ではなく、対象がわざと入れた再現可能な揺れです。運用のガベージコレクションや隣接ワークロードの負荷が、その場所を代わりに担うと考えればよいのです。

同じコードどうしでも、これだけ開く

ステップ1の5回の実行のうち、p50が最も小さい実行と、最も大きい実行を選んでください。/root/lt-regression/02-illusion.txtに、6行を書きます。fastest_run=<번호>・slowest_run=<번호>・fastest_p50=<밀리초>・slowest_p50=<밀리초>・apparent_change_pct=<(느린 값 - 빠른 값) ÷ 빠른 값 × 100, 소수 첫째 자리>・same_code=yesです(プレースホルダーは、番号、ミリ秒、遅い値と速い値を使った式で小数第1位です)。ミリ秒は小数第3位まで書きます。この2つは、同じバージョンを同じ条件で測ったものです。

sort -k2 -n 01-noise.tsvでソートすれば、両端がすぐに出ます。ここで出るパーセンテージが、そのまま、「何も変えなかったとき、1回の測定で主張できる変化量」です。固定しきい値をそれより小さく取ると、ゲートはデプロイをランダムに止めることになります。

5%遅くなったバージョンを5回測る

同じ/opt/lab/lt/lt-regression/svc.pyを、VERSION=small DELAY_MS=21 JITTER_MS=20 SEED=1234で127.0.0.1:8081に追加で起動してください(8080はそのままにします)。ステップ1とまったく同じ負荷を5回かけて、/root/lt-regression/small/run1.csvからrun5.csvまで保存し、/root/lt-regression/03-small.tsvに、ステップ1と同じ形で5行を書いてください。そして/root/lt-regression/03-median.txtに、median_of_medians=<다섯 p50 의 중앙값, 소수 셋째 자리>の1行を書きます(プレースホルダーは、5つのp50の中央値で小数第3位です)。中央値の定義は、ステップ1と同じです。

負荷条件を変えると、2つのバージョンを比べられません。-nと-cを、ステップ1とまったく同じにしてください。5つの値の中央値は、ソートして3番目の値です。1回の実行の値ではなく、5回の実行の中央値を使う理由は、運の悪い実行1つが判定をひっくり返さないようにするためです。

50%遅くなったバージョンも5回測る

/opt/lab/lt/lt-regression/svc.pyを、VERSION=big DELAY_MS=30 JITTER_MS=20 SEED=1234で127.0.0.1:8082に追加で起動し、同じ負荷を5回かけて、/root/lt-regression/big/run1.csvからrun5.csvまで保存してください。/root/lt-regression/04-big.tsvに5行、/root/lt-regression/04-median.txtにmedian_of_medians=の1行を、ステップ3と同じ形で書きます。

3つのバージョンが同時に起動していても、互いに邪魔しません。眠るだけの対象なので、CPUを奪い合わないからです。このステップで出る差は、目でも見えるほど大きいのですが、それでも「目に見える」を判定の根拠にしないのが、このラボの要点です。判定は、次のステップのルールが行います。

判定ルールを文章で書いて、3つのバージョンに適用する

/root/lt-regression/05-rule.txtに、4行を書いてください。metric=p50・stat=median_of_5・noise_ms=<1단계 다섯 실행의 최대 - 최소, 소수 셋째 자리>・rule=<이 규칙을 한 문장으로, 40자 이상>です(プレースホルダーは、ステップ1の5回の実行の最大−最小で小数第3位、このルールを1文で40文字以上です)。そして/root/lt-regression/05-verdict.tsvに、3行を書きます。各行はタブで区切った3列<이름> <delta_ms> <judgment>で(プレースホルダーは名前です)、名前はbase・small・bigの順です。deltaは、そのバージョンの5回の実行の中央値から、baseの5回の実行の中央値を引いた値(小数第3位)で、judgmentは、deltaがnoise_msより大きいときregression、そうでなければnoiseです。

baseの行のdeltaは、自分自身との差なので0.000です。smallがどちらに判定されるかは、先に決めないでください。その日に測ったノイズが決めます。それが、固定パーセンテージのしきい値との違いです。不等号は「大きい」であって、「以上」ではありません。

ルールをCIゲートとして固定する

/root/lt-regression/gate.shを作成してください。bash gate.sh <기준선 디렉터리> <새 측정 디렉터리>で呼び出すと(プレースホルダーは、ベースラインのディレクトリと、新しい測定のディレクトリです)、2つのディレクトリの*.csvをそれぞれ読んで、実行ごとにp50を求め、各グループの中央値を出します。出力は1行で、delta=<밀리초> noise=<밀리초> REGRESSIONまたはdelta=<밀리초> noise=<밀리초> OKです(プレースホルダーはミリ秒です)。deltaは、新しい測定の中央値からベースラインの中央値を引いた値、noiseは、ベースラインの実行のp50の最大から最小を引いた値で、どちらも小数第3位までです。deltaがnoiseより大きければ終了コード1、そうでなければ0で終了します。採点ツールは、自分で作った3セットの入力でこのスクリプトを回して、合格と失敗の両方を確認します。

ディレクトリの中のcsvの個数を、あらかじめ決めておかないでください。採点ツールは、5個ではないグループでも回します。csvが1つもなければ、0で終了せず、別の終了コードで知らせてください。新しい測定のほうが速い場合(deltaが負)は、回帰ではありません。値を数字で比べるときは、awk 'BEGIN{exit !(d > n)}'のように、awkの終了コードを使ってください。

1回だけの比較は、同じコードも回帰にしてしまう

ステップ1の5回の実行のうち、p50が最も小さいものを/root/lt-regression/one-base/run.csvに、最も大きいものを/root/lt-regression/one-new/run.csvにコピーしてください(2つのファイルとも、baseバージョンの測定です)。その2つのディレクトリでゲートを回して、出力を/root/lt-regression/07-gate-out.txtに保存し、/root/lt-regression/07-onerun.txtに、5行を書きます。noise_ms=<게이트가 낸 값>・delta_ms=<게이트가 낸 값>・gate_exit=<종료 코드>・false_positive=<yes 또는 no>・min_runs=<기준선을 최소 몇 번 돌려야 한다고 볼 것인가, 2 이상의 정수>です(プレースホルダーは、ゲートが出した値、終了コード、yesまたはno、ベースラインを最低何回回す必要があると見るかで2以上の整数です)。

ベースラインの実行が1つだけなら、最大と最小が同じなので、ノイズが0になります。しきい値が0なら、0より大きい差は、何でも回帰です。このステップで回帰と報告された2つの測定は、同じバージョンを、同じマシンで、同じ条件で測ったものです。min_runsには、自分のルールで使う、最低の反復回数を書いてください。

元に戻すか: ゲートの答えと、人の答え

ゲートをさらに2回回してください。bash gate.sh base bigの出力は/root/lt-regression/08-gate-big.txtに、bash gate.sh base baseの出力は/root/lt-regression/08-gate-same.txtに保存します。そして/root/lt-regression/08-decision.txtに、6行を書いてください。big_exit=・same_exit=・rollback=<yes 또는 no, big 판을 되돌릴 것인가>・min_runs=<5 이상의 정수>・threshold_rule=<문턱을 어떻게 정할 것인가, 40자 이상>・why=<60자 이상>です(プレースホルダーは、yesまたはnoでbigバージョンを元に戻すか、5以上の整数、しきい値をどう決めるかで40文字以上、60文字以上です)。

同じデータを自分自身と比べれば、差は0なので、どんなルールでも回帰であってはなりません。ゲートがその場合でも止めるなら、ルールではなく、実装が間違っています。threshold_ruleには、固定パーセンテージの代わりに何を使うかを書いてください。whyには、元に戻すと決めた(または決めなかった)根拠を、前のステップの数字とつなげて書きます。