#!/usr/bin/env bash # Regenerates every file in docs/output/ from a clean build. Run from the repo root. # # Requires: GraalVM for JDK 25 on PATH (JAVA_HOME pointing at it), Maven, Python 3. # Builds JVM jars first, benchmarks them, then builds native images (slow - budget # 20-30 minutes total on a 2-vCPU machine) and benchmarks those too. # # This is exactly the sequence used to produce the committed docs/output/ files - # re-run it yourself and your numbers should land in the same ballpark, modulo your # own hardware. See README.md for the one GraalVM build-arg fix the Micronaut module # needed and why. set -euo pipefail cd "$(dirname "$0")/.." echo "== 1/4: building JVM artifacts ==" (cd spring-boot-app && mvn -q -B clean package -DskipTests) (cd quarkus-app && mvn -q -B clean package -DskipTests) (cd micronaut-app && mvn -q -B clean package -DskipTests) echo "== 2/4: JVM startup + memory (4 runs each, first discarded) ==" { echo "=== Spring Boot JVM startup+memory (4 runs) ===" python3 scripts/measure.py "spring-boot-jvm" 8081 /widgets 4 -- java -jar spring-boot-app/target/spring-boot-app.jar echo "=== Quarkus JVM startup+memory (4 runs) ===" python3 scripts/measure.py "quarkus-jvm" 8082 /widgets 4 -- java -jar quarkus-app/target/quarkus-app/quarkus-run.jar echo "=== Micronaut JVM startup+memory (4 runs) ===" python3 scripts/measure.py "micronaut-jvm" 8083 /widgets 4 -- java -jar micronaut-app/target/micronaut-app.jar } | tee docs/output/01-startup-memory-jvm.txt echo "== 3/4: JVM throughput (15s @ concurrency 20, one app at a time) ==" run_throughput() { local label="$1" port="$2" ready_path="$3"; shift 3 "$@" > "/tmp/${label}.log" 2>&1 < /dev/null & local pid=$! disown for i in $(seq 1 60); do code=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}${ready_path}" --max-time 2 || true) [ "$code" = "200" ] && break sleep 0.2 done python3 scripts/throughput.py "$label" localhost "$port" "$ready_path" 15 20 kill -9 "$pid" 2>/dev/null || true sleep 1 } { echo "=== Spring Boot JVM throughput ===" run_throughput spring-boot-jvm 8081 /widgets java -jar spring-boot-app/target/spring-boot-app.jar echo "=== Quarkus JVM throughput ===" run_throughput quarkus-jvm 8082 /widgets java -jar quarkus-app/target/quarkus-app/quarkus-run.jar echo "=== Micronaut JVM throughput ===" run_throughput micronaut-jvm 8083 /widgets java -jar micronaut-app/target/micronaut-app.jar } | tee docs/output/02-throughput-jvm.txt echo "== 4/4: native builds + benchmarks (slow) ==" (cd quarkus-app && mvn -q -B -Pnative clean package -DskipTests && mvn -q -B package -DskipTests) (cd micronaut-app && mvn -q -B -Pnative clean package -DskipTests && \ mvn -q -B -Pnative package org.graalvm.buildtools:native-maven-plugin:compile -DskipTests) (cd spring-boot-app && mvn -q -B -Pnative clean package -DskipTests && mvn -q -B -Pnative native:compile) { echo "=== Spring Boot NATIVE startup+memory (4 runs) ===" python3 scripts/measure.py "spring-boot-native" 8081 /widgets 4 -- ./spring-boot-app/target/spring-boot-app echo "=== Quarkus NATIVE startup+memory (4 runs) ===" python3 scripts/measure.py "quarkus-native" 8082 /widgets 4 -- ./quarkus-app/target/quarkus-app-runner echo "=== Micronaut NATIVE startup+memory (4 runs) ===" python3 scripts/measure.py "micronaut-native" 8083 /widgets 4 -- ./micronaut-app/target/micronaut-app } | tee docs/output/03-startup-memory-native.txt { echo "=== Spring Boot NATIVE throughput ===" run_throughput spring-boot-native 8081 /widgets ./spring-boot-app/target/spring-boot-app echo "=== Quarkus NATIVE throughput ===" run_throughput quarkus-native 8082 /widgets ./quarkus-app/target/quarkus-app-runner echo "=== Micronaut NATIVE throughput ===" run_throughput micronaut-native 8083 /widgets ./micronaut-app/target/micronaut-app } | tee docs/output/04-throughput-native.txt echo "Done. See docs/output/05-summary.txt for the consolidated table (written by hand from the runs above - update it if your numbers differ materially)."