Exploratory local HTTP comparison, October 1, 2026

CN1 source: 60c4f310bddcfc51e5acc75c63a57c43c73430d3
Host: Apple M4 Max, 16 logical CPUs, 64 GiB RAM, macOS 26.6.2
Spring Boot: 4.1.1, spring-boot-starter-webmvc, default embedded Tomcat.
Native CN1: build.sh -O3, current runtime, JSON_MODE=0 per-request LinkedHashMap,
REUSE_RESPONSE=1. macOS platform-thread fallback; packaged deployment targets Linux.
Spring JVM: Zulu JDK 25, default GC and heap, no AOT cache or explicit heap limit.
Spring native: Oracle GraalVM 25.0.4+7.1, Maven native profile, default optimization,
no PGO and no -march=native. See versions and hashes in results.json.
All measurements run locally, never CI.

Reproduce on a Mac with JDK 8, JDK 25, GraalVM 25, clang, Maven, Python 3 and wrk.
Set BENCH_ROOT to an empty work directory and JDK_8_HOME / JDK_25_HOME / GRAAL_HOME.
Check out the CN1 source commit above. From its repository root:

JAVA_HOME="$JDK_8_HOME" PATH="$JDK_8_HOME/bin:$PATH" \
  CN1_BACKEND_STANDALONE_DEMO=1 CN1_BACKEND_DEMO=demo/bench \
  ./vm/backend/build.sh Bench com.demo "$BENCH_ROOT/cn1-bench" -O3

Create $BENCH_ROOT/spring/src/main/java/example; copy App.java there and copy
spring-pom.xml to $BENCH_ROOT/spring/pom.xml.

JAVA_HOME="$JDK_25_HOME" PATH="$JDK_25_HOME/bin:$PATH" \
  mvn -f "$BENCH_ROOT/spring/pom.xml" package
JAVA_HOME="$GRAAL_HOME" PATH="$GRAAL_HOME/bin:$PATH" \
  mvn -f "$BENCH_ROOT/spring/pom.xml" -Pnative native:compile

Build https://github.com/wg/wrk under $BENCH_ROOT/wrk, then run:
python3 run.py

Method: three rotating arm orders, a new server per arm per round. First-response
startup includes a validated HTTP response and millisecond readiness polling.
Filesystem caches were not flushed. Load generator and server share the host;
no CPU affinity or dedicated-host guarantee. Plaintext precedes JSON each round.
Every route is response-validated, warmed for 5 seconds, then measured for 10.
wrk: -t4 -c32; RSS sampled using ps every 100 ms while measuring.
RSS is neither peak footprint nor live heap. No perf claim follows from binary size.
No TLS, database, authentication, tracing, or application workload in these routes.
This is not a steady-state JVM, full-framework parity or Linux deployment study.

Both implementations disable practical per-connection request caps. CN1 WORKERS=32,
CN1_HTTP_KEEPALIVE_BURST=1000000000. Tomcat max-keep-alive-requests=-1.
The first default CN1 trial was rejected: 6272 read errors among 1613085 responses
on plaintext. No data from that rejected trial appears in the summary.
The runner rejects any measured wrk output containing socket or non-2xx errors.
Accepted raw wrk outputs and all nine samples are included.
