30秒で判断する
- 結論
- 別マシンで得た絶対ビルド時間を、そのまま言語やツールチェーンの横断ランキングに使うべきではありません。同じversionと4 CPUまで揃えても環境差の倍率は一定になりませんでした。
- 向いている条件
- 異なるOSやマシンのbenchmarkを比較するときに、CPU数だけ揃えれば公平になるのか、再現性をどこまで確認すべきか判断したい場合に役立ちます。
- 測っていないもの
- WindowsとLinuxで同一CPU、同一filesystem、同一バックグラウンド負荷にはしていません。OSそのものの優劣やGoとJavaの一般的な速度差を測る実験ではありません。
一台のWindows PCで10個のツールチェーンを測ると、きれいな順位表は作れる。 しかし、その順位がツールチェーンそのものを表しているとは限らない。
前回の広い比較には、Windows側の常駐プロセス、CPU割り当て、ファイルI/O、OS固有の実装差を十分に分離できていない問題があった。 そこで、その結果は結論から外し、両環境で同じversionを用意できたGo 1.23.2とJava 21.0.11だけを測り直した。
今回はWindowsとLinuxの両方を4 logical CPU相当に制限し、同じ8個のleaf sourceを持つ小規模fixtureについて、独立した3 batchを実行した。 各batchではproject-cold buildと1ファイル変更後のincremental buildを7回ずつ測っている。
結果は、CPU数を合わせれば環境差を補正できるという期待を崩した。 GoのRelease相当cold buildはWindowsでbatch中央値883.6から1,020.5 ms、Linuxでは72.4から79.2 msだった一方、Java 21.0.11の差はそれほど大きくなかった。
同じversionと4 CPUだけを残した
比較対象はGo 1.23.2とJava 21.0.11だけである。 WindowsとLinuxの両方で同じversionを確認でき、同じfixtureとbuild optionを使えることを条件にした。
Windows側はWindows 11 x64で、benchmark runnerを4 logical CPUへaffinity固定した。 CPUの製品名、搭載メモリ容量、マシン全体のcore数は比較に必要ないため公開しない。 常駐プロセスは完全には排除できていない。
Linux側はDebian 13のKVM環境で、cgroup上のCPU quotaを4 CPUとして使った。 こちらもCPU製品名やメモリ容量は比較条件に含めていない。
4 logical CPU affinityと4 CPU quotaは、同じ計算能力を保証する条件ではない。 したがって、この測定はWindows対Linuxの性能勝負ではなく、別環境で絶対時間の順位がどこまで再現するかを見る検査として扱う。
1 batchを7回で構成した
fixtureは8個のleaf sourceとmainから構成し、外部dependencyを持たない。 mainは各leafを1,000回呼び、build後に同じmarkerを出力できることを確認している。
project-coldではプロジェクト固有の出力と中間生成物を削除するが、OS cacheと共有compiler cacheは消していない。 incrementalでは直前のbuild treeを残し、leaf sourceを1ファイルだけ意味を変えない形で更新してから再buildした。
Goは共有build cacheの初回生成をproject固有のbuild時間へ混ぜないため、各modeの計測前に1回だけ計測外でwarm upした。
Javaはdebug metadataありをjavac -g、metadataなしをjavac -g:noneとして測ったため、Javaの二つのmodeはoptimizerのDebugとReleaseを意味しない。
各環境で7回の測定を1 batchとし、batchそのものを3回独立に繰り返した。 公開している数値は匿名化済みのbatch集約データから生成している。
Release相当cold buildの3 batch
次のグラフは、各batch内7回のmedianを並べたものである。 GoとJavaでは時間の尺度が違うため、二つのパネルで別々の縦軸を使っている。
| Toolchain | Environment | Batch 1 | Batch 2 | Batch 3 | Batch中央値の中央値 |
|---|---|---|---|---|---|
| Go 1.23.2 | Linux / 4 CPU quota | 79.2 ms | 72.4 ms | 78.7 ms | 78.7 ms |
| Go 1.23.2 | Windows / 4 logical CPU affinity | 925.0 ms | 883.6 ms | 1,020.5 ms | 925.0 ms |
| Java 21.0.11 | Linux / 4 CPU quota | 485.6 ms | 518.9 ms | 558.8 ms | 518.9 ms |
| Java 21.0.11 | Windows / 4 logical CPU affinity | 916.3 ms | 869.0 ms | 865.2 ms | 869.0 ms |
GoのLinux側は3 batchが72.4から79.2 msに収まり、Windows側は883.6から1,020.5 msまで動いた。 さらにWindowsのGoでは3 batch目のp95が1,517.2 msまで伸びており、同じ4 logical CPU affinityでも外乱を無視できない。
Javaは別の形になった。 Linux側は485.6から558.8 ms、Windows側は865.2から916.3 msで、環境間の差はGoより小さい。
もし単純なマシン性能差だけが支配しているなら、GoとJavaでWindows対Linuxの倍率は近い値になるはずである。 実際にはbatch中央値の中央値でGoが約11.8倍、Javaが約1.7倍となり、その仮定には合わなかった。
incrementalでも差の形は変わらない
1ファイル変更後のincremental buildでも、GoとJavaで環境差の倍率は揃わなかった。
| Toolchain | Environment | Batch 1 | Batch 2 | Batch 3 | Batch中央値の中央値 |
|---|---|---|---|---|---|
| Go 1.23.2 | Linux / 4 CPU quota | 79.2 ms | 80.1 ms | 81.6 ms | 80.1 ms |
| Go 1.23.2 | Windows / 4 logical CPU affinity | 911.6 ms | 864.7 ms | 1,052.6 ms | 911.6 ms |
| Java 21.0.11 | Linux / 4 CPU quota | 409.7 ms | 445.0 ms | 471.0 ms | 445.0 ms |
| Java 21.0.11 | Windows / 4 logical CPU affinity | 769.2 ms | 725.8 ms | 724.4 ms | 725.8 ms |
Goは今回の小規模単一packageでは、coldとincrementalがほぼ同じ時間になった。 Javaは両環境とも1ファイルだけ再コンパイルするincrementalの方が短くなった。
ここでもGoの環境差は約11.4倍、Javaは約1.6倍である。 CPU数だけを揃えて別マシンの絶対時間を正規化する方法にはならなかった。
前回の10言語順位を撤回した理由
前回のWindows測定そのものが偽の数値だったわけではない。 そのPCで、その時点に、そのbuild commandが何msかかったかという観測値ではある。
問題は、その観測値から「Cが何位、Rustが何位、Zigが何位」という一般的な順位を作ったことである。 Windows側の外乱を十分に制御せず、しかもLinuxで同じtoolchainを再現していない状態では、その順位の原因をtoolchainへ帰属できない。
今回のGoだけでも、versionと4 CPUを揃えたあとにWindows側の3 batch中央値が約14%動いた。 さらにWindowsとLinuxの差がGoとJavaで大幅に違うため、マシン差を一つの係数で補正する方法も使えない。
したがって、旧10言語測定の生データは公開対象から外し、記事の結論にも使わない。
次の多言語比較は一つのLinux環境で測る
多言語のbuild速度そのものを比較するなら、同じOS、同じCPU quota、同じfilesystem、同じ測定batchの中でtoolchainだけを変える方が原因を狭められる。
Linux側で同じversionを用意できるZig、Rust、Go、Javaなどを増やせば、少なくとも「別PCの環境差」と「toolchain差」を一つの表へ混ぜずに済む。 その場合も1回のmedianだけではなく、今回と同じように独立batchを複数回取り、batch間の揺れを結果と一緒に残す。
今回の再計測で確定したのは、GoとJavaのどちらが一般に速いかではない。 別マシンの絶対build時間を、そのまま言語ランキングへ変換する根拠が足りなかったという点である。