29分

ISUCON14でCodexがClaudeの記録に届かなかった背景を、実装とログから調べた

isuconcodexclaudeai-agentsperformance

今回のCodex構成は、Claudeの過去記録のスコアに届きませんでした。 ただ、調べるほど「Claudeのモデルが強いから」で片付けるべきではないと分かりました。ハードウェアと永続化の条件が異なり、過去の最終アプリコードも残っていません。一方で、私たちの改善内容と協調制御には、次に直せる具体的な不足が見つかりました。

2026年9月14日、ISUCON14を題材にAstra単体とAstra+GPT-5.6 Solの協調構成を、独立したAWS環境で各3時間・各1回、同時実行しました。時間内最高合格スコアは単体133,401、協調48,123。Claude側は新たに走らせず、保存されているスコア台帳、設定、変更の記録を追いました。

この記事では、確認できた差と原因の仮説を分けます。前回のISUCON11比較とは別の問題・別の試行です。

ISUCON14は何を速くする問題か

ISUCONは、Webサービスの機能を保ちながら、限られたサーバーで性能を改善する競技です。ISUCON14の「ISURIDE」は、利用者と椅子型の車両を結び付ける配車サービスでした。

位置情報の更新、配車、乗車状態の通知、決済、履歴や売上の集計が同時に動きます。例えば通知のたびに何度もDBへ問い合わせたり、位置情報を1件ずつ書き込んだりすると、配車を増やしたところで処理が詰まります。SQL単体の速度に加え、1回の乗車を完了するまでに必要なDB操作を減らすことが重要になります。

今回の試行には公式アプリの初期版、マニュアル、当該試行の計測結果だけを渡しました。Claudeの過去解答は渡していません。ベンチマーカーのソースコードも読まず、マニュアルとベンチ出力を判断材料にしました。

スコアを見る前に、比較できる範囲を揃える

指標Astra単体Astra+Sol協調Claudeの過去記録
初期スコア1,1179713回平均3,091
今回の180分内に完了した最高合格値133,40148,123不明
過去の開始側時刻が180分内の最高合格値537,835
再起動後126,549、合格ベンチ未実行966,809、別の長時間試行
アプリ用EC2c5.large×3c5.large×3c7a.large×3
ベンチ用EC2c5.2xlargec5.2xlargec7a.2xlarge→後半にc7a.4xlarge
記録合計token58,501,61568,658,038不明

今回の時間枠は00:16:12〜03:16:12 JST。停止や待ち時間も3時間に含めています。各群にアプリ3台+ベンチ1台を割り当てました。ただし、本番のベンチ環境であるECS FargateをEC2で代用しており、本番と完全同一ではありません。ベンチプロセスのメモリは8 GiBに制限しました。

Claudeの台帳時刻は、ログ初期化とベンチ実行より前に取得されています。537,835点は記録開始時刻2:56:47の走行ですが、終了時刻は不明です。見出しに使われていた965,214点は、ベンチ機増強後、報告上約5時間12分の値でした。この報告時間は夜間の空白を圧縮したもので、最初から最後までの実時刻差は13時間55分04秒です。

モデルの来歴にも差があります。今回の親子は実際のthreadからAstra/highとSol/highを監査しました。過去の「Claude Opus 5 (1M context)」はcommitの共同作成者表記が根拠で、全操作に使った実モデル、子の数、トークンは分かりません。これは3モデルを同条件で競わせたランキングではありません。

序盤の閾値到達と、その後の伸びは違った

今回のAstra単体・協調と、条件の異なるClaude過去記録の時間対スコア推移
今回のAstra単体・協調と、条件の異なるClaude過去記録の時間対スコア推移

青・橙はベンチ完了時刻、紫は過去の記録開始側時刻です。紫の曲線は参考線として見てください。

今回の単体は10,000点に7.83分、50,000点に35.91分で到達しました。Claudeの参考時刻はそれぞれ38.47分、81.85分です。起点や環境が違うため速度の優劣は断定できませんが、少なくとも「Codexは初期調査が遅く、何もできなかった」という説明には合いません。

その後、Claude記録では約1時間22分に103,038点、約1時間41分に272,757点へ進んでいます。今回の単体は100,000点への到達が94.90分、最終的な最高値が133,401点でした。見るべきなのは、最初のインデックス追加の速さよりも、途中からどこまで処理構造を変えられたかです。

仮説1:DBへ行かなくて済む処理を、十分に増やせなかった

Claudeの変更履歴には、3時間より前に次の候補を測った記録があります。

記録上の経過候補の内容合格スコア
0:51:34rides.statusを持たせ、状態を求めるクエリを削減40,498
1:21:51ライドと状態遷移をプロセス内に保持103,038
1:41:00通知をSSE化272,757
2:07:47中間状態でのrides更新を撤廃488,400

これは各施策だけの改善量ではなく、当時の逐次的な採用経路です。過去の最終アプリcommit 9bf45053 は見つからないため、Go/SQLの実装を取り出して今回と同じ条件で検証することもできません。

今回も、両方式はインデックス、N+1削減、一括マッチング、SSE、距離の永続集約、DBの別ホスト化まで進んでいます。SSEを知らなかった、キャッシュを全く使わなかった、という差ではありません。 問題は、それらを入れた後も、通知・状態遷移・集計にどれだけDB操作が残るかです。

採用済みの実コードでは、単体は座標のまとめ書き、通知確認のまとめ書き、通知先列による直接検索、椅子統計の永続集約、椅子認証のキャッシュ、履歴のJOINも持ちます。協調側には、この追加経路の多くがありません。両方とも通知の未送信状態などをSQLで保持しており、過去記録の広いプロセス内状態管理と同じ構造とは確認できません。

したがって最も検証価値が高いのは、残っているDB往復・書込みを、1乗車当たりで数え直すことです。状態をメモリに置くだけでは、初期化や再起動後の正しさを失う可能性があります。正本をどこに置き、いつ永続化し、どう再構築するかまでを一つの候補として測る必要があります。

仮説2:MySQLの永続化条件が違う

過去のsetupにはinnodb_flush_log_at_trx_commit=2、binlog無効、buffer pool 2 GiB、redo 1 GiB、MySQL側の接続上限1,024の設定が残っています。今回の単体はbinlogを無効にしてもflush-at-commitを1に保ち、協調側も既存の永続化設定を維持しました。今回のアプリ側接続プールは単体40、協調48で、MySQL側の接続上限とは別の値です。

MySQL公式資料では、1はcommitごとにログを書き出してディスクへflushし、2はcommitごとの書込みと通常1秒ごとのflushを分けます。2では障害時に未flushのトランザクションを失い得ます。つまり、性能と永続化の条件が変わります。MySQL公式資料

書込みの多い経路で性能差を生み得る設定ですが、この設定差が何点分だったかは未測定です。過去の設定ファイルだけでは、すべての歴史的走行の実効値も証明できません。正常な再起動を通ったことは、電源断やOS障害時のデータ保持と同じ検証ではありません。

過去の高得点設定をそのままコピーする前に、問題の要求を満たす永続化条件を定め、その条件を揃えて比較すべきです。今回の記事から「2にすれば勝てる」「過去の結果は失格」といった結論は出しません。

仮説3:3台を用意したが、3台を活用できていない

今回の選定版は、両方式ともapp1がnginx+Go+matcher、app2がMySQL、app3にはサービス処理の役割がありませんでした。Claudeの最終設定はapp1をGo、app2をMySQL、app3をnginx・TLS・静的配信に分けています。

ただし、ここも「nginxを移せば解決」とは言えません。今回の最高値を出したrunのCPUサンプルは次の通りでした。

CPU busyの平均Astra単体Astra+Sol協調
app1:nginx+Go47.3%39.3%
app2:DB78.3%85.1%
app3:役割なし1.7%2.2%

各ホスト17回のtopサンプルから得た記述値で、CPU時間の厳密な積分ではありません。それでも、app1が全面的にCPU不足だったという説明より、DB側の処理と待ち時間を優先して調べる根拠になります。単一スレッドの飽和やロック待ちは別途確認が必要です。

app3が空いていることは、配置の探索が残った証拠です。ただし、その活用でどれだけ改善するかは、DBの制約と分けて測る必要があります。過去の452,030点の段階にはapp3へのTLS・静的配信分離とnginxのworker_connections=65535が記録されており、nginx移動だけの効果とは扱えません。

3時間の差に使ってはいけない説明

Claudeの最終記録で印象的なのは、公開スコア式を目的関数に取り入れたマッチングです。しかし、これは報告上4時間55分以降。今回の単体・協調は迎車ETAを中心に割り当てていましたが、その差を持ち出して537,835点との3時間の隔たりを説明するのは時系列が逆です。

同様に、DB接続プールを200へ制限して835,763点になった記録は3時間20分、ベンチ機の増強は4時間02分付近です。これらは長時間の最終結果を理解する材料であり、3時間以前の高得点を説明する施策ではありません。

ただし、3時間前のマッチングにも差があります。過去記録は0:38に全件一括、1:52までに全組greedyと飢餓しきい値の調整へ進んでいます。今回の選定コードが一度に扱う未割当rideは単体最大100件、協調32件。候補集合の広さや長く待つ利用者の扱いは、3時間前の差を説明し得る別の仮説です。歴史側の実装は照合できず、寄与点数も不明です。

目的関数の改善は、次に試す価値のある仮説です。ただし今回の分析では、3時間内にすでに行われていた状態管理・書込み削減・通知処理を先に比較します。初期スコアで割って改善倍率を揃えても、負荷やボトルネックは線形に変わらないため、機種差を除去した正規化にはなりません。

協調側は、子を動かすことより改善を測り切ることに苦戦した

今回の単体は通常の合格計測47件、協調側は21件でした。合格には改善せず棄却した候補も含みます。通常の不合格runは両群0件、未完了は各1件。これとは別に診断計測が8件と5件あります。全体として協調側は、時間内に正しく測った候補の数が少なくなりました。

親Astraは公式Codex Python SDK 0.154.0を通じて同じthreadを継続し、協調側だけが外部coordinatorからSolのCodex CLI workerを起動しました。native subagentではありません。ChatGPT Proの認証を使った実行で、Agents SDKからAPIモデルを呼ぶ構成でもありません。

親子の制御は次の流れです。

  1. 親が仮説、基準commit、担当ファイル、検証方法を短いタスクとして渡す。
  2. 子は専用cloneで作業する。割当外の書込み、Gitメタデータの変更、toolのネットワーク通信をsandboxで制限する。
  3. 子が差分と根拠を返す。親がコンパイル・レビューし、coordinatorが候補をcommitする。
  4. brokerと実験実行器が、配備・初期化・ベンチ・証跡回収を環境ごとに排他実行する。
  5. 同じ仮説・基準版の修正は同じthreadを再利用し、基準版が変われば新しいattemptに分ける。

27子thread、31回の実行を監査し、全子がSol/highでした。起動前の隔離検査も全31回で成功。同時実行の観測最大は2体です。ただし締切で止めた親turnに終了イベントがなく、全時刻の並行度を完全には復元できませんでした。

この仕組みに、二つの停止原因がありました。

不具合起きたこと影響
準備失敗の応答と台帳の不整合executorのエラー応答にmanifest fieldがなく、coordinatorが例外終了taskがmeasuringに残り、約13.39分後に運用者が終端処理
同一threadの修正候補でGit refが衝突保存refにturn番号がなく、前候補へのfetchがnon-fast-forward運用者が旧・新候補を保全し、既存実行器で同じ候補を測って復旧

時計は延長していません。完全無人の比較ではなく、この制御障害も協調方式の結果に含まれます。ただし、停止がなければ何点になったかは分かりません。47対21の差を、すべてこの二件のせいにすることもできません。

子の文脈を小さくしても、親の仕事は減らなかった

累積記録tokenに対する最高合格スコア。Astra単体とAstra+Sol協調
累積記録tokenに対する最高合格スコア。Astra単体とAstra+Sol協調

token単体協調
input(cached込み)58,281,03668,271,152
cached input56,694,01665,778,304
uncached input1,587,0202,492,848
output(reasoning込み)220,579386,886
合計58,501,61568,658,038

協調側は17.36%多くのtokenを記録しました。これは会話1回のコンテキスト長ではなく、各呼出しの入力・出力の累計です。cached inputとreasoning outputを再加算していません。

子へ渡すpromptは中央値4,580 bytes、最大6,893 bytes。各子threadの最大入力tokenの中央値は32,587、全子の最大は85,372でした。全会話を渡さない工夫は機能していました。

それでも、協調側の親だけで62,367,072 tokenを使っています。単体側の全量より多く、子の合計6,290,966 tokenを加える前から差があります。文脈分離と、親の判断・レビュー・例外処理を減らすことは別の課題でした。累積tokenだけで作業時間の内訳は断定できませんが、委譲が親の処理量を減らした証拠にはなりません。

ここからの改善案は、子を増やすことより、親へ返す結果を「候補commit、差分、検証結果、未解決点、次に必要な判断」に揃え、ビルド結果・失敗終端・台帳の整合確認を機械処理へ寄せることです。効果は次の計測で確認する必要があります。

Codex SDKを使ったから負けたのか

今回の単体も協調も同じSDKを使っています。SDKを使わない群との比較はしていないので、SDKが原因だとは言えません。確認できた二つの停止原因は、私たちのcoordinatorとexecutorの境界にありました。

公式資料では、Python SDKはローカルのCodex app-serverをJSON-RPCで制御し、配布版は対応するruntimeを固定します。これは会話や実行をプログラムから扱う仕組みです。Codex SDK公式資料

どの性能仮説を優先するか、子に何を任せるか、候補をいつ計測するか、失敗した台帳をどう戻すかは、今回私たちが実装した側の責任です。SDK導入を、そのまま最適化能力の向上と見なしていたなら、その期待を修正する必要があります。

isucon-harnessは使えていたか、費用と後始末はどうか

使っていました。 親の完了コマンド1,372件、broker応答546件、全89 manifestを照合しました。通常の親による直接SSH・AWS操作や、公式ベンチの直接実行・ソース読取りは確認されませんでした。

プロビジョニングからセットアップ、build・deploy、ベンチ、ログ回収、最終再起動、destroyまで、既存のMake/lib/AWS経路を使っています。候補commitを固定し、配備したバイナリと実行中ELFのhashも照合しました。前述の運用者による復旧と、終了後の読み取り診断は別記録です。

ただし、単体の再起動後は126,549点で合格した一方、協調側はベンチ前に終了しました。DBが準備できる前にGoが接続して落ち、systemdの再起動待ちの瞬間をidentity検査が失敗と判定しました。後続の読み取りでは自動回復と選定バイナリの一致を確認しましたが、再起動後のスコアは未取得です。時間内の結果保存と、プロトコルの全条件達成は分けて扱います。

EC2の計算料金推計とスコアの推移。モデル料金やEBS等を含まない
EC2の計算料金推計とスコアの推移。モデル料金やEBS等を含まない

保存した単価で、3時間のEC2計算料金は各群2.247、合計2.247、合計4.494です。準備から撤収確認までを含む計算料金の保守的な上限推計は合計約$5.05。EBS、Public IPv4、転送、税、モデル・サブスクリプション等は含まず、請求総額ではありません。ProのtokenをAPI単価へ換算した架空の費用比較もしていません。

8台のEC2と関連リソースはdestroyし、AWSへの独立した再照会で残存ゼロを確認しました。

次に何を変えるか

今回の結果から、次の順で手を入れるのが妥当だと考えています。いずれもこの記事で新たに実験済みという意味ではありません。

優先変更・検証判定に使うもの
1準備失敗、修正候補ref、起動待ちを含む制御器の失敗経路を直すtaskが必ず終端する、同じ候補を一意に追える、再起動後ベンチへ進める
2DB操作を1乗車当たりで集計し、支配的な経路を選ぶSQL回数・書込み・commit・待ち時間、初期化と再起動後の正しさ
3永続化条件を維持した状態集約・まとめ書きを一つずつ比較する同じ基準版・設定の合格スコア、DB負荷の減少
4nginx/TLSのapp3分離を単独候補として測るapp1の余力、DB制約、スコアの変化
5親の結果確認を短くし、レビュー・ビルド・終端判定を自動化する合格候補数/時、待ち時間、親の入力token
6公開採点条件に沿ったマッチング目的関数を比較する完了乗車・移動・待ち時間と、公式スコアの変化

Claudeをモデルとして比較し直すなら、同じ初期版・機種・永続化条件・3時間の時計で新たに実行し、実モデルと全session usageも保存する必要があります。過去の施策を移植して効き方を調べる検証は有用ですが、解答を見ないモデル比較とは別の実験になります。

現時点で確認できたのは、採用済みコードにDB往復・書込みとapp3活用の検証余地が残り、協調側は単体より計測完了候補が少なかったことです。これらが何点分だったかは分離できていません。ハードウェアと永続化の差を揃えずに、その残りをモデル能力の差と呼ぶこともできません。

データと根拠

公開用集計JSON今回の走行CSVClaudeの過去スコアCSV根拠ファイルと分析境界を添付しました。画像は実験のRESULTと同じ集計から生成したものです。

元の根拠はexperiments/isucon14-codex-compare/RESULT.md、確定したsource-comparison.final.mdcommand-path-audit.final.mdproblems/isucon14/RESULT.mdと旧スコア台帳です。公開版には必要な集計と根拠の所在・hashを載せ、認証情報や生の会話ログは含めていません。各方式1回のため、スコアのばらつき、個別変更の因果効果、一般的なモデル優位は推定していません。