時間がどこで使われるかを言う
レイテンシとは、リクエストを送ってから答えが返るまでの時間です。モデルの呼び出しでは、最初のトークンまでの待ち時間と、最後のトークンまでの待ち時間という二つの数があります。ユーザーは、平均ではなく最も遅い瞬間で機能を判断するため、重要です。
十九人には一秒で返り、二十人目には八秒かかる機能は、その二十人目には壊れているように見えます。
このレッスンのゴール
メモ
私は、モデル呼び出しで時間がどこに使われるかを説明し、記録した所要時間からタイムアウトを決められます。
キーワード
| ことば | いみ |
|---|---|
| レイテンシ | リクエストに答えが返るまでの時間 |
| 最初のトークン | ストリーミング応答の最初の一片 |
| パーセンタイル | リクエストの一定の割合がそれ以下に入る値 |
| テール | 最も遅い、ごく少数のリクエスト |
| タイムアウト | あきらめるまでの最長の待ち時間 |
| ストリーミング | 応答を作りながら少しずつ送ること |
学生が入力するセルは、このプレビューでは空白です。
パーセンタイルはテールを見せる
平均は、遅いリクエストを隠してしまいます。代わりに、所要時間を並べ替えます。中央値(p50)は真ん中の値です。p95 は、リクエストの 95 パーセントがその値以下に入る値です。それより上の少数がテールです。
最近順位法を使います。n 個の値があるとき、n の p パーセントを切り上げた位置(1 から数える)の値を取ります。補間をしないので、どの道具でも同じ答えになります。
例: 二十個の所要時間
| 指標 | 20 個の中の位置 | 値 |
|---|---|---|
| p50 | 10 番目 | 900 ms |
| p90 | 18 番目 | 2300 ms |
| p95 | 19 番目 | 4200 ms |
| p99 | 20 番目 | 7800 ms |
| 平均 | なし | 1493 ms |
学生が入力するセルは、このプレビューでは空白です。
データからタイムアウトを決める
タイムアウトは、待ってもよい最長の時間です。短すぎると、よい応答まで切ってしまいます。長すぎると、止まった呼び出しがユーザーと接続を抱え込みます。p95 のような高いパーセンタイルを取り、余裕を足して、きりのよい値に丸めます。
ストリーミングは待ち時間を隠します。ユーザーは数百ミリ秒で最初のトークンを読み、残りはその間に届きます。最初のトークンと応答全体は別々に測り、それぞれに上限を決めましょう。
よくある間違い
注意
「平均なら数が一つで簡単だ」と、平均からタイムアウトを決める人が多くいます。すると、この例では 20 件中 4 件、つまり 5 分の 1 が切れてしまいます。記録した所要時間の高いパーセンタイルから決め、それで何件が切れるかを確かめましょう。
レイテンシの報告を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 記録された架空の所要時間(ミリ秒)を二十個。応答全体と、最初のトークン。' 'const TOTAL = [820, 910, 760, 1500, 880, 940, 830, 4200, 900, 870, 1020, 790, 850, 2300, 910, 880, 960, 840, 7800, 900];' 'const FIRST = [210, 240, 200, 300, 220, 250, 215, 500, 230, 225, 260, 205, 222, 380, 240, 228, 255, 218, 900, 232];' > latency4.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
レイテンシの報告を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 最近順位法。並べ替えて、「n の p パーセント」を切り上げた位置の値を取る。' 'function percentile(values, p) {' ' const sorted = values.slice().sort(function (a, b) { return a - b; });' ' const rank = Math.ceil(p * sorted.length / 100);' ' return sorted[rank - 1];' '}' 'function average(values) {' ' return Math.round(values.reduce(function (sum, v) { return sum + v; }, 0) / values.length);' '}' >> latency4.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
レイテンシの報告を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// タイムアウト。95 パーセンタイルに 20 パーセントの余裕を足し、100 ms 単位に切り上げる。' 'function timeoutFor(values) {' ' return Math.ceil(percentile(values, 95) * 120 / 100 / 100) * 100;' '}' 'function over(values, limit) {' ' return values.filter(function (v) { return v > limit; }).length;' '}' >> latency4.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
レイテンシの報告を書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const limit = timeoutFor(TOTAL);' 'console.log(\"応答全体: p50 \" + percentile(TOTAL, 50) + \"、p90 \" + percentile(TOTAL, 90) + \"、p95 \" + percentile(TOTAL, 95) + \"、p99 \" + percentile(TOTAL, 99) + \" ms\");' 'console.log(\"平均 \" + average(TOTAL) + \" ms、最長 \" + Math.max(...TOTAL) + \" ms\");' 'console.log(\"最初のトークン: p50 \" + percentile(FIRST, 50) + \"、p95 \" + percentile(FIRST, 95) + \" ms\");' 'console.log(\"タイムアウト \" + limit + \" ms では \" + TOTAL.length + \" 件中 \" + over(TOTAL, limit) + \" 件が切れる\");' 'console.log(\"2000 ms のタイムアウトなら \" + TOTAL.length + \" 件中 \" + over(TOTAL, 2000) + \" 件が切れる\");' >> latency4.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
報告を実行する
docker exec -w /home/node/project agent node latency4.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
この所要時間は記録されたものです
ここでは、わざと呼び出しを遅くしたりしません。二十個の所要時間は記録された架空の値なので、全員が同じ数を得ます。本物のシステムでは、最初のレッスンのように、トレースからこれを読みます。
この Run はモデルを呼びません。助っ人は要らず、代わりに使うものもありません。
Run の出力
| 行 | 示していること |
|---|---|
| 応答全体: p50 900、p90 2300、p95 4200、p99 7800 ms | 応答全体の形 |
| 平均 1493 ms、最長 7800 ms | 平均は、遅いテールに引き上げられている |
| 最初のトークン: p50 230、p95 500 ms | 半数が 230 ms 以内に最初のトークンを見て、95 パーセントが 500 ms 以内。応答全体よりずっと早い |
| タイムアウト 5100 ms では 20 件中 1 件が切れる | p95 に 20 パーセントを足して 100 に切り上げ |
| 2000 ms のタイムアウトなら 20 件中 3 件が切れる | 当てずっぽうでは、よい応答まで切れる |
学生が入力するセルは、このプレビューでは空白です。