サンプルコードで身につける: ビルドが遅いと言われる理由を測って分ける
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
12.32 秒をログの時刻で区切る
依存取得済みの を4回分、ログの時刻差で区切った表です(単位はミリ秒)。4回ともほぼ同じ形で、いちばん大きいのは の約1.1秒、次が の起動とプロジェクト読み込みの計約0.7秒でした。 が本体の2ファイルをコンパイルした区間は 244〜258 ms です。
区間 run2 run3 run4 run5ログに出ない時間(起動と終了) 約400 約415 約413 約416プロジェクト読み込み(POM の解釈) 278 286 269 280clean:clean 18 18 17 19resources:resources 24 24 26 23compiler:compile(javac、2ファイル) 247 249 258 244compiler:testCompile(1ファイル) 47 47 46 46surefire:test 1117 1094 1103 1107jar:jar 90 90 89 90spring-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秒台でした。差は待ち時間(主にネットワークと考えられます)で、初回が何秒かは回線と配布元の状態に左右されます。
a-cold 1 100.17 13.19 0.96 418776a-cold 2 57.30 13.94 0.98 391544a-cold 3 220.57 13.38 0.97 365280b-clean-package 1 9.44 8.55 0.30 396436b-clean-package 2 2.32 7.06 0.21 364580b-clean-package 3 2.32 6.96 0.21 364428b-clean-package 4 2.32 6.91 0.22 366440b-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 のように毎回新しい環境でビルドするとデーモン無しのほうの数字になります。
Mavenclean 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.271ファイル変更して 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.99validate(何もしない) 0.60 / 0.60 / 0.61 / 0.61 / 0.61javac を直接(2ファイル) 0.33 / 0.32 / 0.31 / 0.32 / 0.33Gradleclean build(デーモン無し・初回) 4.31 / 4.50 / 4.39clean build --no-daemon 4.16 / 4.22 / 4.23clean build(デーモン有り) 1.90 / 1.74 / 1.56 / 1.68 / 1.511ファイル変更して build 1.58 / 1.41 / 1.54 / 1.71 / 1.48変更なしで build 0.50 / 0.40 / 0.37 / 0.38 / 0.36 ※全タスク UP-TO-DATEclean 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 秒で、どれも最小アプリの値であって言語の順位ではありません。
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.491.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回起動しました。ただし がほぼ無いこのアプリでは全体の差は揺れに埋もれており、実際のアプリで何秒変わるかはここからは言えません。
A) javac の規模依存(合成ソース。1クラス20メソッド・83行)クラス数 / 行数 壁時計(秒) user CPU(秒)100 / 8,300 行 0.41 / 0.41 / 0.42 1.35〜1.441,000 / 83,000 行 0.86 / 0.91 / 0.89 3.21〜3.405,000 / 415,000 行 2.40 / 2.43 / 2.35 6.73〜7.01B) @SpringBootTest を8クラス(mvn test)コンテナの起動回数 最初のクラス(秒) 2つ目以降の各クラス(秒) mvn test の壁時計(秒)8クラスとも同じ構成 1 0.962 / 1.119 / 1.085 0.002〜0.012 2.11 / 2.79 / 2.658クラスが別々の構成 8 1.008 / 0.963 / 0.788 0.045〜0.098 2.82 / 2.75 / 2.27