Research
LYR Performance Note #002 計測が導いた旅

去年の正解が、今年の間違いになる

同じ計測をやり直したら、ボトルネックは動いていた

2026-07-22 シリーズ第2回 6 min レイテンシgeoセルフホスト計測

数か月後に同じ計測をやり直したら、内訳は真逆になっていた。生成は全体の1割。残る9割は、待ち行列と距離だった。

前回いちばん効いたレバーは、もう的外れだった。ボトルネックは動く——正解には賞味期限がある。

そこで往復を消そうとユーザーの近くでの推論(エッジ推論)を試すと、今度は全体が3倍遅くなった。部分最適は、全体では負ける。

前回、遅さの原因はモデルの生成——正確には、モデルが答える前に”考えて”いたことだった、と書いた。思考をオフにして(reasoning=none)、生成は一気に軽くなった。当時はそれで十分だった。

問題は、正解に賞味期限があることだ。

習慣が、昔の結論を疑わせる

一度「分解して測る」を覚えると、それが癖になる。別の作業のついでに、私はまた同じ内訳計測を回した。深い意図はなく、ただ「いまはどうなってるか」を見たかっただけだ。

出てきた数字を見て、手が止まった。

区間p50割合
ネットワーク往復275 ms~55%
queue(待ち行列)167 ms~33%
生成(generation)50 ms~10%
prompt(入力処理)7 ms~1%

かつて時間を支配していた生成が、いまや10%まで縮んでいた。しかも新旧2機種(各38サンプル)でほぼ一致しているので、これは測定ノイズではなく構造だ。

理由はすぐ分かった。前回モデルの”考え”を止め、その後も出力を絞り込んで、生成そのものが軽くなっていた。生成を軽くしたぶん、相対的に「待ち」と「距離」が浮上したのだ。支配項が入れ替わっていた。

待ち行列は、平均で語ってはいけない

さっきの167msは、待ち行列の中央値にすぎない。そして待ち行列は、平均で語ってはいけないものの典型だった。それを痛感したのは、いまの現行モデルではなく、その一つ前に本番候補だった別のモデルでのことだ。

そのモデルは、共有インフラ上の巨大なモデルだった。混雑時に、同じ待ち行列を測ると——中央値からして、桁が違った

パーセンタイル待ち行列(別モデル・混雑時)
p50(中央値)988 ms
p903,438 ms
p994,883 ms

このモデルは、中央値ですでに約1秒——現行モデル(167ms)の、およそ6倍だ。そして100回に1回は、約5秒待たされる。しかも厄介なのは、いつそうなるかが読めないことだ——空いている時間帯なら150ms、混雑すればこの分布。「いつも速い」でも「いつも遅い」でもなく、読めない

平均だけ見ていたら、このテールはまったく見えない。だが、ユーザーが覚えているのは平均ではなく、その最悪の一回だ——マニフェストの3番目の原則——良い代理指標は、平均でなく最悪の一回(tail)を見る——そのものである。そしてこのテールは、外部の共有インフラを使う限り消せない——並ぶ相手を、こちらは選べないからだ。(専有すれば、このテールごと消える。それが第5回。)

ネットワークの275msをさらに分解すると、接続はコネクションプールで再利用されていて確立コストはほぼゼロ、応答も小さく転送も一瞬。つまり275msはほぼ純粋な地理的往復——端末から遠い場所(当時は米国)にある推論サーバまでの、物理的な往復時間だった。(世界各地のAWSリージョンで実測した詳細は、深掘り「地理は、レイテンシの動かせない床だ」に。)

だから、打つべきレバーも入れ替わる

前回の正解「モデルに考えさせない」は、もうこれ以上は効かない。10%しかない生成をどういじっても、体感はほとんど動かない。いま支配的なのは、

  • 距離(往復275ms)→ 推論をユーザーの近くに置けば消える。この275msは、動かせない物理の”床”だ
  • queue(167ms)→ 共有インフラの待ち行列。専有すれば消える

つまりレバーは「モデルを速く」でも「出力を削る」でもなく、推論サーバーを物理的に近く・専有で持つことに移っていた。

仮説を検証する(そして、また負ける)

「往復さえ消せば速くなる」なら、ユーザーの近くのエッジで推論すればいい。都合よく、あるエッジ推論サービスにまったく同じモデルが載っていた。すでにそのエッジ基盤を経由していたので、叩き先を差し替えるだけで比較できる。理屈の上では完璧だった。

結果はこうだ。

経路翻訳 wall p50max結果
現行(遠いが専用の高速推論)502 ms✅ 字幕が出る
エッジ推論(近いが汎用GPU・同一モデル)1500 ms(制限時間に到達)2610 ms❌ 字幕が出ない

決定的な負け。往復を250msも消したのに、全体は遅くなった。

原因は、分解表のおかげで即座に判明した。近くても、エッジの汎用GPUでは生成が間に合わなかった。暖機後の純粋な生成でも現行より遅く(測れた範囲で約1.5倍)、実運用ではコールドスタートや待ちも重なって、翻訳が終わる前に制限時間(1500ms)で打ち切られた——だから字幕が出ない。表の「1500ms」は生成の速さではなく、クライアント側の制限時間そのものだ。往復をいくら消しても、生成が締め切りを越えれば、全体では負ける。10%まで小さくなっていたはずの生成が、遅いハードに載せた瞬間に勝敗を決めたのだ。

唯一の出口は「近い×速い」の両立

ここで、前回のもうひとつの教訓が効いてくる。部分最適は罠だ。往復だけ消しても、生成が遅ければ意味がない。逆に、生成だけ速くても、遠ければ意味がない。

だから条件は「近接かつ高速推論」の両立になる。試算では、専用の高速チップをユーザーに近い地域(例:国内)に置ければ、往復は十数ミリ秒、全体は100ms前後まで落ちる見込みだった。これが、自前で推論を持つ(セルフホスト)を検討し始めた本当の起点だ。速いモデルが欲しかったのではない。近くて速い場所が欲しかった。

(では、なぜ最初から自前にしなかったのか——それは供給側の事情という別の物語で、第4回に続く。)

教訓

  1. ボトルネックは動く。だから測り続ける。 一度の分解で得た正解は、状況が変われば失効する。ズレに気づけたのは計測が習慣になっていたからで、前回の結論を「もう分かったこと」として棚に上げていたら、いまも効かないレバーを引いていた。
  2. 相対値で考える。 生成が「軽くなった」のではなく、他が相対的に「重くなった」。支配項は、絶対値でなく割合で入れ替わる。
  3. 部分最適は罠。 往復を消しても生成が遅ければ逆効果。全体(end-to-end)で、複数の区間を同時に満たさないと勝てない。

付録:生データ

項目実測条件・留保
翻訳 wall p50502msPixel 10 / 7a 各 n=38、cross-device で一致
内訳(P10 / 7a)ネットワーク往復 275 / 268ms、queue 167 / 200ms、generation 50 / 66ms、prompt 7ms生成は全体の約1割
ネットワーク内訳接続はプール再利用で +0〜2ms、転送 +1〜3ms275ms は純・地理往復
エッジ推論 A/B(同一モデル)wall p50 1500ms(全サイクルが上限に到達)/ max 2610ms(現行 502ms)暖機後でもエッジ生成は現行の約1.5倍遅い
近接×高速の試算往復 ~13ms、全体 ~97ms体感の壁 ~177ms を初めて下回る見込み(試算)