ある開発者が、PlaywrightやPuppeteerを通じてChrome DevToolsデバッガーをアタッチすると、fetchベースのアップロードが20倍以上もスロットリング(制限)されることを発見しました。これにより、日常的なパフォーマンスベンチマークが誤解を招くデータへと変わってしまいます。
調査のきっかけとなった謎
Cofferは、サーバーに送信する前にデータを暗号化するブラウザベースのファイルストレージであり、ローカルネットワーク経由で日常的にギガビット級のアップロードを行っています。チームがアップロード速度を測定した際、ある乖離が見つかりました。ダウンロードは回線を飽和させている一方で、アップロードは利用可能な帯域幅の約8分の1という低速な状態だったのです。この不一致により3回にわたってコードの変更が行われましたが、状況を改善することはできませんでした。ところが、4回目の「修正」によって劇的な向上が見られたかと思いきや、デバッガーを取り除くとその効果は消失してしまいました。
チームが最初に試したこと
エンジニアたちは、よくある原因を調査しました。
- チャンクサイズ – ブロックを16 MiBから32 MiBに倍増させても、スループットは変わりませんでした。
- パイプライン処理 – 次のブロックの暗号化を現在のアップロードとオーバーラップさせましたが、13%というわずかな向上にとどまり、測定誤差の範囲内でした。
- 並行性 – 複数のアップロードを並列で実行しても、合計速度は同じ上限に達し、全体的な制限があることが示唆されました。
これらのいずれのバリエーションも、8倍もの低速化を説明できるものではありませんでした。
驚くべき直接比較
問題を特定するために、チームはクライアントの実装を切り替えました。同じネットワーク上で.NET HttpClientを使用すると700 Mbpsを記録しましたが、Chromiumのfetch() APIから同じリクエストを実行すると140 Mbpsで停滞しました。この鮮明なコントラストは、ブラウザのネットワークスタックが原因であることを示唆していましたが、次の実験によってその結論は覆されました。
デバッガーの隠れたコスト
PlaywrightとPuppeteerは、Chrome DevTools Protocol (CDP)を介してChromeを制御します。このプロトコルはブラウザプロセスにデバッガーをアタッチし、ネットワークイベント、DOMスナップショット、コンソールログを公開します。チームは、Uint8Arrayのペイロードを送信するfetch()呼び出しについて、CDPデバッガーをアタッチした場合とそうでない場合で、絞り込んだテストを実施しました。
- デバッガーをアタッチした場合: 113 Mbps
デバッガーが存在することで、アップロード速度は20倍以上低下しました。デバッガーをアタッチしていない通常のEdgeウィンドウでの手動テストでは600 Mbps以上に達し、ブラウザ自体は負荷がなければトラフィックを処理できることが確認されました。
控えめながら、確かな改善
低速化の大部分はデバッガーによるものでしたが、チームは真の最適化も発見しました。リクエストボディをUint8ArrayからBlobに切り替えることで、Chromiumの速度が約**30%**向上したのです。これは有用な調整ではありますが、当初期待していた「奇跡的な」ブーストには遠く及びません。
なぜこれがエンジニアにとって重要なのか
- 計測ツールは嘘をつくことがある。 ブラウザを自動化するパフォーマンスツール自体が、測定チェーンの一部となります。
- 不可能に見えるベンチマークは、整合性の確認(サニティチェック)が必要である。 数値がネットワーク容量から大きく逸脱している場合、測定環境を第一の疑いとして検討すべきです。
- 「変化なし」という結果にも価値がある。 変更が何ももたらさないことを確認できれば、実体のないバグを追いかける無駄な労力を防げます。
- 手動での確認は安価な保険である。 通常のブラウザウィンドウで同じ操作を実行することで、隠れた計測オーバーヘッドを明らかにできることがあります。
反論:デバッガーが不可欠な場合
デバッガーは、それなしではアクセスできないページ動作、エラートレース、ネットワークのタイムラインへの可視性を提供します。回帰テスト、セキュリティ監査、または複雑なUIインタラクションにおいては、CDPデバッガーのアタッチはしばしば不可欠です。重要なのは、機能テストと生のパフォーマンス測定を切り離し、後者が目的である場合にはデバッガーを無効にすることです。
まとめ: 自動テストを可能にするツールは、パフォーマンスを歪める最大の要因にもなり得ます。ブラウザ、ネットワーク、あるいはコードを責める前に、デバッガーがデータストリームを密かにスロットリングしていないかを確認してください。
