From 079d6f67398ecd1f3a44b8a1f9620b0e4141c0e7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 22 Aug 2026 11:33:36 +0200 Subject: [PATCH] Oversubscribe test forks in CI to cut wall clock time `:test` is 83% of CI wall clock (8m09s of a 9m47s build). The shared `RewriteJavaPlugin` sets `maxParallelForks = availableProcessors()`, which is 4 on `ubuntu-latest`. Those 4 forks finished at 3m47s, 4m55s, 7m27s and 8m07s -- 24m16s of executor time that would take 6m04s if evenly packed, so roughly two minutes went to imbalance alone. Gradle hands out whole test classes, and 47 of the 267 test classes resolve Maven poms over the network, so a couple of long latency-bound classes landed late on two forks with no work left to steal. Running 1.5 forks per core gives finer granularity and lets the CPU-bound classes fill the gaps while the network-bound ones block. Heap drops to 1500m so the extra forks still fit: 6 x 1500m is ~9g of a 16g runner, where 6 x 2g would not have been comfortable. GitHub scales runner RAM at 4g per core, so 1.5 forks/core at 1500m stays within budget on larger runners too. Local dev behaviour is unchanged -- the plugin's `availableProcessors() / 2` and the 2g heap still apply when `CI` is unset. --- build.gradle.kts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index c05b606231..4d4cfeddf5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -115,7 +115,13 @@ tasks.withType(Javadoc::class.java) { } tasks.test { - maxHeapSize = "2g" // Set max heap size to 2GB or adjust as necessary + maxHeapSize = "2g" + if (System.getenv("CI") != null) { + // 47 of the 267 test classes resolve Maven poms over the network, so forks idle on I/O; + // oversubscribing the runner's cores packs the remaining work more evenly across them. + maxParallelForks = Runtime.getRuntime().availableProcessors() * 3 / 2 + maxHeapSize = "1500m" + } } tasks.withType {