Dev Study
← 解説「ビルドが遅いと言われる理由を測って分ける」に戻る

サンプルコードで身につける: ビルドが遅いと言われる理由を測って分ける

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

12.32 秒をログの時刻で区切る

依存取得済みの を4回分、ログの時刻差で区切った表です(単位はミリ秒)。4回ともほぼ同じ形で、いちばん大きいのは の約1.1秒、次が の起動とプロジェクト読み込みの計約0.7秒でした。 が本体の2ファイルをコンパイルした区間は 244〜258 ms です。

mvn clean package の内訳(ms。区間 = その --- 行から次の --- 行まで。Maven 3.9.16 / JDK 25)表
区間 run2 run3 run4 run5
ログに出ない時間(起動と終了) 約400 約415 約413 約416
プロジェクト読み込み(POM の解釈) 278 286 269 280
clean:clean 18 18 17 19
resources:resources 24 24 26 23
compiler:compile(javac、2ファイル) 247 249 258 244
compiler:testCompile(1ファイル) 47 47 46 46
surefire:test 1117 1094 1103 1107
jar:jar 90 90 89 90
spring-boot:repackage 97 95 97 93
ログ上の合計 1920 1905 1907 1904
壁時計(/usr/bin/time) 2320 2320 2320 2320

2初回の3回: 壁時計は約4倍違い、CPU 時間はほぼ同じ

を空にしてからの mvn package を3回測った結果です。は 100.17 秒、57.30 秒、220.57 秒と大きく揺れましたが、CPU を使った時間(user)は3回とも13秒台でした。差は待ち時間(主にネットワークと考えられます)で、初回が何秒かは回線と配布元の状態に左右されます。

logs/mvn/times.tsv(条件・回・壁時計 秒・user 秒・sys 秒・最大 RSS KB。/usr/bin/time で計測)コマンド出力
a-cold 1 100.17 13.19 0.96 418776
a-cold 2 57.30 13.94 0.98 391544
a-cold 3 220.57 13.38 0.97 365280
b-clean-package 1 9.44 8.55 0.30 396436
b-clean-package 2 2.32 7.06 0.21 364580
b-clean-package 3 2.32 6.96 0.21 364428
b-clean-package 4 2.32 6.91 0.22 366440
b-clean-package 5 2.32 6.85 0.23 362468

3条件を変えると何秒になるか(Maven と Gradle)

同じアプリで条件だけを変えた壁時計の一覧です(全回の値)。Maven は変更の大小に関係なく約2.3秒で、何も変えなくても約2.0秒かかりました。 はの有無で同じ build が 4.2〜4.5 秒と 1.5〜1.9 秒に分かれ、CI のように毎回新しい環境でビルドするとデーモン無しのほうの数字になります。

条件別の壁時計(秒・全回。依存取得済み。Maven 3.9.16 / Gradle 9.7.1 / JDK 25)表
Maven
clean package(テストあり) 9.44 / 2.32 / 2.32 / 2.32 / 2.32 ※1回目は clean プラグインの初回取得込み
clean package -DskipTests 1.25 / 1.25 / 1.25 / 1.26 / 1.27
1ファイル変更して package 2.31 / 2.30 / 2.31 / 2.32 / 2.28
変更なしで package 2.04 / 2.05 / 2.03 ※テストは毎回走る
変更なしで package -DskipTests 1.00 / 0.98 / 0.99
validate(何もしない) 0.60 / 0.60 / 0.61 / 0.61 / 0.61
javac を直接(2ファイル) 0.33 / 0.32 / 0.31 / 0.32 / 0.33
 
Gradle
clean build(デーモン無し・初回) 4.31 / 4.50 / 4.39
clean build --no-daemon 4.16 / 4.22 / 4.23
clean build(デーモン有り) 1.90 / 1.74 / 1.56 / 1.68 / 1.51
1ファイル変更して build 1.58 / 1.41 / 1.54 / 1.71 / 1.48
変更なしで build 0.50 / 0.40 / 0.37 / 0.38 / 0.36 ※全タスク UP-TO-DATE
clean bootJar(テスト無し) 0.42 / 0.53 / 0.42

4nest build・cargo build と並べる(条件つき)

同じ応答を返す NestJS と Rust のアプリを、同じ機械で測った値です。依存取得済みのは Rust の release が 5.5 秒で Maven の 2.32 秒より長く、これは Rust が依存クレートもソースからコンパイルし(Compiling の行は自分を含めて51)、Java は依存をコンパイル済みの で受け取るためです。逆に変更なしでは Rust が 0.02 秒で、どれも最小アプリの値であって言語の順位ではありません。

壁時計(秒・全回)。node 24 + NestJS 12 系 / rustc 1.98.1 + axum 0.8.9 / Maven 3.9.16。CPU 10 の arm64 Docker表
Maven(Spring Boot) nest build(NestJS) cargo build --release(Rust)
初回(依存の取得から) 100.17 / 57.30 / 220.57 npm ci: 3.61 / 4.54 / 8.77 24.51 / 39.85 / 26.68
依存取得済みのクリーンビルド 2.32(テストあり) 0.73〜0.74 5.46 / 5.50 / 5.49
1.25〜1.27(テスト無し) (user CPU は約26秒)
1ファイル変更 2.28〜2.32(テストあり) 0.73〜0.74 0.68〜0.79
変更なし 2.03〜2.05(テストあり) (測っていない) 0.02
依存の数 実行時の JAR 34本 389 パッケージ 55 パッケージ
(本番依存のみで 98) (コンパイルは51)

5追加の2実験: 行数を増やした javac と、Spring を起動するテスト8本

に依存しない合成ソースでは、javac は 415,000 行でも 2.4 秒前後でした。 を8にすると、構成が同じならの起動は1回で済み(キャッシュされる)、構成が1つずつ違うと8回起動しました。ただし がほぼ無いこのアプリでは全体の差は揺れに埋もれており、実際のアプリで何秒変わるかはここからは言えません。

追加実験(Docker 再起動後の系列。JDK 25 / Boot 4.1.1。各3回)表
A) javac の規模依存(合成ソース。1クラス20メソッド・83行)
クラス数 / 行数 壁時計(秒) user CPU(秒)
100 / 8,300 行 0.41 / 0.41 / 0.42 1.35〜1.44
1,000 / 83,000 行 0.86 / 0.91 / 0.89 3.21〜3.40
5,000 / 415,000 行 2.40 / 2.43 / 2.35 6.73〜7.01
 
B) @SpringBootTest を8クラス(mvn test)
コンテナの起動回数 最初のクラス(秒) 2つ目以降の各クラス(秒) mvn test の壁時計(秒)
8クラスとも同じ構成 1 0.962 / 1.119 / 1.085 0.002〜0.012 2.11 / 2.79 / 2.65
8クラスが別々の構成 8 1.008 / 0.963 / 0.788 0.045〜0.098 2.82 / 2.75 / 2.27

図やコードに出てくる用語(タップで説明)