自作CLIツールwipとDocker DesktopをWindows上で同条件ベンチマークしてみた結果 - yubeleのスライド |
slidict.io

EN | JA

自作CLIツールwipとDocker DesktopをWindows上で同条件ベンチマークしてみた結果

Google Translate: Japanese English
yubele
yubele
フォロワー 0人
Last updated: 2026/09/18
読む時間: 00:21

共有

Embed code

通報

  • 自作CLI「wip」とDocker Desktopを同条件で公平に比較したい

  • 憶測ではなく、起動時間・応答速度・CPU負荷を実測で判断する

  • Windows上の計測ハーネス自体にも落とし穴が多く、結果を歪めやすい

  • wipはWSLCをラップする薄いOSS CLIで、Docker Desktop代替を目指している

  • 4構成を比較: Windows/WSL × Docker Desktop/wip

  • Node.js固定レスポンスアプリに同時20・120秒の負荷をかけて測定

  • 指標は起動・停止・rps・p50/p95レイテンシ・ホストCPU/メモリ

  • 計測方針は「wipが速いと決めつけず、結果から判断する」

  • 各構成で1ウォームアップ+3ラウンド、実行順はランダム化

  • 他人のコンテナを止めないガードや、Dockerのグレースフル停止を追加

  • localhostのIPv6問題、ReadToEndのデッドロック、CSVロケール依存を解消

  • 定常状態ではwipが優位: スループットは約4割増、p50レイテンシは約半分

  • ホストCPU使用率中央値もDocker Desktop約44%に対し、wipは約20〜21%

  • 一方でアプリ起動〜ReadyはDocker Desktopが速く、特にWSLからのwipは遅い

  • Docker DesktopはWSLから起動すると、同じエンジンでもバックエンド起動が約2倍遅かった

  • WSLから実行したときのwipの起動遅延を重点的に分析する

  • 未実施のストレージ計測や、複数マシンでの反復実験を追加する

  • 整備したハーネスの安全策を、Windows向けCLIや計測基盤にも横展開する

Source document

title: "自作CLIツールwipとDocker DesktopをWindows上で同条件ベンチマークしてみた結果"
emoji: "⚡"
type: "tech"
topics: ["docker", "wsl", "windows", "powershell", "benchmark"]
published: true
---

この記事でわかること

Windows Subsystem for Linux Containers(WSLC)をラップした自作OSS CLI [wip](https://github.com/slidict/wip) と Docker Desktop を、同一PC・同一アプリ・同一負荷条件で計測した結果と、その計測用ハーネスをPowerShellで組む過程で踏んだ、他の環境でも再現しうる落とし穴を共有します。

  • スループット・レイテンシ・ホストCPU使用率の実測比較(4構成 × 1ウォームアップ+3ラウンド)

  • 「IPv6優先のlocalhost解決でHTTPが固まる」「stdout/stderrの逐次ReadToEndでデッドロックする」「CSVが実行環境のロケールで壊れる」など、ベンチマークハーネスやCLIツールをWindows上でPowerShell/.NETで書くときに踏みやすい問題と対処

背景: wipとは

wip は、Microsoft WSLC(wslc.exe / wslc)を [dip](https://github.com/bibendi/dip) ライクなワークフローでラップするOSS CLIです。プロジェクトのコンテナ・イメージ・環境変数・コマンドを1つの wip.yml にまとめ、シェル補間を経由しない安全な引数配列として wslc に渡します。自前のオーケストレーションエンジンは持たず、あくまで wslc の薄いラッパーという設計です。

version: 1
mode: container
container: app
dependencies:
  app:
    image: slidict/slidict:development
    workdir: /app
    command: server
    ports:
      - "3000:3000"
    volumes:
      - ".:/app"
interaction:
  rails:
    type: exec
    command: bin/rails
    container: app
    interactive: true

Docker Desktopに代わる選択肢として開発しているため、「実際どれくらい速いのか/重いのか」を憶測で語らず、きちんと計測したいというのがこのベンチマークの動機です。使い方の全体像は、slidict.io上に公開している紹介スライドにもまとめています。

やったこと: ベンチマーク設計

同一PC上で次の4構成を比較しました。

| ID | コマンド実行元 | コンテナバックエンド |
|---|---|---|
| windows-docker | Windows PowerShell | Docker Desktop |
| windows-wip | Windows PowerShell | wip / WSLC |
| wsl-docker | WSL Ubuntu Bash | Docker DesktopのWSL統合 |
| wsl-wip | WSL Ubuntu Bash | wip / WSLC |

設計方針として、計測プロトコル(.claude/skills/wip-benchmark/SKILL.md)に「結果から判断する。wipが軽い・速いと先に決めつけない」と明記し、フェアな比較になるよう条件を揃えています。

  • テスト対象アプリはフレームワーク・依存ゼロの固定レスポンスNode.jsサーバー(benchmark/app/server.js)。バックエンドの差ではなくコンテナ基盤の差を測るのが狙いです。

  • 1ウォームアップ + 3計測ラウンドを構成ごとに実施、実行順は各ラウンドでランダム化

  • 負荷生成は同時実行数20、120秒間

  • baseline / backend-onlyのidle / app-idle はそれぞれ60秒観測

  • 計測項目: バックエンド起動時間・アプリ起動〜Ready時間・停止時間・スループット(rps)・エラー率・p50/p95レイテンシ・ホストCPU/メモリ(約1Hzサンプリング)

  • 実行環境: Windows 11 Pro 10.0.26200 / 12th Gen Core i7-12700KF (12コア/20論理) / RAM 63.86GB / WSL 2.9.12.0 (kernel 6.18.40.1-1) / Docker 29.8.0 / wip 2.5.2

詰まった点と解決

ハーネス自体のPRレビュー(自動レビューで30件指摘)に対応する中で、他のベンチマーク/CLIツール開発でも再現しうる問題がいくつも見つかりました。特に有用だったものを挙げます。

1. 「他人のコンテナを巻き込んで強制終了する」事故

開発途中、ベンチマークのbaselineリセット処理が、このベンチマークが作ったのではない既存のDockerコンテナまで巻き込んで強制終了させてしまう事故が実際に起きました。対策として、対象バックエンドを強制サイクルする前に「このベンチマークが作成していないコンテナ/セッションが動いていないか」を確認し、あれば処理を拒否するガード(Assert-NoForeignDockerContainers / Assert-NoForeignWslcContainers)を追加。Docker Desktopの停止も、いきなり Stop-Process -Force ではなく、まず CloseMainWindow() によるグレースフル終了を試みてからフォールバックする形に変更しました。

2. localhost がIPv6(::1)に解決されてHTTPが固まる

計測対象PCでは localhost::1 を先に解決し、公開ポートへのIPv6ループバック通信がタイムアウトせず単純にハングする(TCP自体は繋がるがHTTPが応答しない)事象がありました。127.0.0.1 を明示することで解決しています。

3. stdout/stderrの逐次 ReadToEnd() によるデッドロック

プロセスの標準出力・標準エラーを順番に ReadToEnd() していると、標準エラー側のパイプが先に埋まった場合にデッドロックする可能性があります。両ストリームを並行して読み取るように変更しました。

4. Ready判定はHTTPステータスだけでなくボディも見る

ポートに残留した無関係なリスナーが200を返すと、それをアプリのReadyと誤検知してしまいます。レスポンスボディの内容まで検証するようにしました。

5. CSVが実行環境のロケールで壊れる

数値を文字列化する際にカルチャ依存のフォーマットを使うと、小数点がカンマになるロケールで results.csv / samples.csv が壊れます。不変カルチャ(invariant culture)でフォーマットするよう修正しました。

6. Windows Modern StandbyでバックグラウンドプロセスがSuspendされる

計測中にWindowsのModern Standbyが働き、ハーネス自身のバックグラウンドプロセスがサスペンド(実質停止)される事象が実測で発生しました。各ラウンドの実行中は SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED) でスリープ・スタンバイを抑止するようにしています。

7. wsl-dockerdocker.sock がWindows側の docker info 成功より遅れる

Docker Desktopを起動直後、Windows側で docker info が成功していても、WSL側の docker.sock がまだ追いついていないことがありました。WSLディストリビューション内部からもポーリングしてから「Ready」と判定するように修正しています。

結果

Windows PowerShellから: Docker Desktop vs wip

| 指標(3ラウンド中央値) | windows-docker | windows-wip |
|---|---|---|
| バックエンド起動→Ready | 3619 ms | 1716 ms |
| アプリ起動→HTTP Ready | 647 ms | 2270 ms |
| アプリ停止 | 385.7 ms | 214.6 ms |
| スループット | 19257 rps | 27021 rps |
| p50 / p95 レイテンシ | 1.04 / 1.40 ms | 0.55 / 1.37 ms |

バックエンド自体の起動はwipの方が速い一方、アプリ起動ステップはDocker Desktopの方が速い結果でした(Docker Desktop側はビルド済みイメージに対する素の docker run、wip側は wip up -d が1回の呼び出しでより多くの処理をしているためと考えられます)。起動後の定常状態では、wipが同一並行数でスループット約4割増・p50レイテンシ約半分でした。

WSL Ubuntuから: Docker Desktop vs wip

| 指標(3ラウンド中央値) | wsl-docker | wsl-wip |
|---|---|---|
| バックエンド起動→Ready | 8114 ms | 1741 ms |
| アプリ起動→HTTP Ready | 1086 ms | 6711 ms |
| アプリ停止 | 736 ms | 4668 ms |
| スループット | 19030 rps | 27855 rps |
| p50 / p95 レイテンシ | 1.05 / 1.42 ms | 0.55 / 1.29 ms |

Windowsからの実行と同じ傾向(wipはバックエンド起動が速くスループット/レイテンシも優位、Docker Desktopはアプリ起動/停止が速い)が出ています。

実行元の違い: 同じバックエンドをWindows/WSLどちらから起動するか

| 指標(中央値) | windows-docker | wsl-docker | windows-wip | wsl-wip |
|---|---|---|---|---|
| バックエンド起動→Ready | 3619 ms | 8114 ms | 1716 ms | 1741 ms |
| アプリ起動+Ready | 647 ms | 1086 ms | 2270 ms | 6711 ms |
| スループット | 19257 rps | 19030 rps | 27021 rps | 27855 rps |

Docker Desktopは、同じエンジンに対してWSLから起動するとWindows PowerShellから起動する場合よりバックエンド起動時間がおよそ2倍(3.6秒→8.1秒)になりました。wsl.exe を経由する分のオーバーヘッドとみられ、同一エンジンでもコマンドの実行元次第で差が出ることが分かります。wip側は実行元による差がほぼありませんでした(1.72〜1.74秒)。一方でアプリ起動+Ready はwipの方がWSL実行時に大きく悪化しており(2.3秒→6.7秒)、良い面だけでなくトレードオフもはっきり出ています。

ホストCPU/メモリ

負荷印加時のホストCPU使用率中央値は、Docker Desktop側の2構成が約44%だったのに対し、wip側の2構成は約20〜21%と、ほぼ半分でした。メモリは計測条件のノイズが大きく(9時間にわたる計測でベースライン自体が±4GB程度変動)、正確な差分としてではなく「Docker Desktop構成の方が待機中・負荷時ともに常駐メモリが目立って多かった」という参考値として報告しています。

この計測の限界(正直に書いておきます)

  • wsl-docker のラウンド3は、本来の連続計測中にDocker Desktop起動待ちで37分以上ハングしてプロセスごと強制終了されたため、約7時間後に単独で再計測し、そのラウンドとしてマージしたものです(周辺環境の条件が他の15試行と完全には揃っていません)

  • ストレージフェーズの計測(プロトコルには定義済み)は今回実施していません

  • 単一マシン・単一計測キャンペーンであり、統計的に繰り返した実験ではありません(同一構成内の3ラウンドはいずれも5%程度のばらつきに収まっています)

まとめ

  • 起動後の定常状態では、Windows/WSLどちらの実行元でも wip が Docker Desktop に対しスループット約4割増・p50レイテンシ約半分・ホストCPU使用率は約半分という結果でした

  • ただし「アプリ起動〜Ready」のステップは逆にDocker Desktopの方が速く、特にWSLから wip up を叩くケースで顕著に遅くなりました。単純に「wipの方が万能に速い」ではなく、ステップごとに得意不得意があるトレードオフです

  • Docker DesktopはWSLから起動すると同一エンジンでもバックエンド起動が約2倍遅くなる、という実行元固有のコストも見えました

  • ベンチマークハーネス自体を書く際に踏んだ「IPv6のlocalhost解決」「stdout/stderrの逐次読み取りによるデッドロック」「CSVのロケール依存フォーマット」「Modern Standbyによるプロセスサスペンド」は、Windows上でPowerShell/.NETの計測・CLIツールを書く際に一般的に再現しうる落とし穴として、他の開発でも参考になるはずです

詳細な生データ・環境情報・実行ログは以下のPRに含まれています。

自作CLIツールwipとDocker DesktopをWindows上で同条件ベンチマークしてみた結果のサムネイル(1ページ目)
1 / 9