Add virtual-threads-benchmark: re-run Spring Boot 4.1 / JDK 25 benchmarks, JEP 491 pinning fixed
Companion module for the rewritten post 'Virtual Threads on Spring Boot 4.1: The Benchmarks, Re-Run, and the Pinning Advice That Expired', retitled and re-benchmarked on Boot 4.1.1 / JDK 25.0.4.1 (the original post was written against Boot 3.4 / JDK 21). Covers: I/O-bound and CPU-bound throughput (platform vs virtual threads, including a JIT-warmup benchmarking bug this build caught and fixed), JEP 491 proof that synchronized no longer pins a virtual thread's carrier across a blocking call as of JDK 24 (obsoleting the old avoid-synchronized advice), proof that -Djdk.tracePinnedThreads=full is inert on JDK 25, and JEP 506's finalized ScopedValue API (JDK 25 GA, no --enable-preview, and a different shape than the old preview API). Kept as its own module rather than a new top-level repository, alongside the existing async/ module, which already has a stronger dual-JDK JEP 491 proof that this module's docs cross-link to instead of duplicating.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
GET /thread-info with spring.threads.virtual.enabled=false vs true
|
||||
==================================================================
|
||||
enabled=false -> Thread: Thread[#9742,http-nio-auto-5-exec-1,5,main] | Virtual: false
|
||||
enabled=true -> Thread: VirtualThread[#9769,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1 | Virtual: true
|
||||
@@ -0,0 +1,7 @@
|
||||
I/O-bound endpoint (/io, Thread.sleep(300)), concurrency=600, 2 vCPU sandbox, Spring Boot 4.1.1 / JDK 25.0.4.1
|
||||
==============================================================================================================
|
||||
platform threads : total=600 success=600 wall=1325ms p50=733ms p99=1201ms
|
||||
virtual threads : total=600 success=600 wall=1042ms p50=658ms p99=720ms
|
||||
|
||||
default embedded Tomcat platform-thread pool = 200; with 600 concurrent 300ms-sleep
|
||||
requests, platform threads must queue in ~3 sequential batches, virtual threads do not.
|
||||
@@ -0,0 +1,8 @@
|
||||
CPU-bound endpoint (/cpu, 20,000x SHA-256), concurrency=60, 2 vCPU sandbox, Spring Boot 4.1.1 / JDK 25.0.4.1
|
||||
============================================================================================================
|
||||
platform threads : total=60 success=60 wall=208ms p50=130ms p99=202ms
|
||||
virtual threads : total=60 success=60 wall=201ms p50=187ms p99=196ms
|
||||
|
||||
a CPU-bound virtual thread never yields -- it stays mounted on its carrier for the full
|
||||
computation, so it competes for the same 2 physical cores a platform thread would. Expect
|
||||
these two wall times to be close, not virtual threads winning decisively as in the I/O case.
|
||||
@@ -0,0 +1,9 @@
|
||||
JEP 491 proof: synchronized held across Thread.sleep, JDK 25.0.4.1
|
||||
==================================================================
|
||||
availableProcessors (default virtual-thread carrier pool size) = 2
|
||||
virtual threads = 60, each holds a DISTINCT monitor for 250ms
|
||||
predicted wall time IF PINNED (pre-JDK-24 behaviour): ~7500ms
|
||||
predicted wall time IF NOT PINNED (JDK 24+ behaviour): ~250ms, independent of carrier count
|
||||
|
||||
actual wall time: 251ms
|
||||
verdict: NOT PINNED -- matches JEP 491's documented JDK 24+ behaviour
|
||||
@@ -0,0 +1,18 @@
|
||||
java -version:
|
||||
openjdk version "25.0.4.1" 2026-08-18 LTS
|
||||
OpenJDK Runtime Environment Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS)
|
||||
OpenJDK 64-Bit Server VM Temurin-25.0.4.1+1 (build 25.0.4.1+1-LTS, mixed mode, sharing)
|
||||
|
||||
-- without -Djdk.tracePinnedThreads=full --
|
||||
java.version=25.0.4.1
|
||||
jdk.tracePinnedThreads=null
|
||||
Running a virtual thread that holds a monitor across Thread.sleep(200)...
|
||||
Done. If jdk.tracePinnedThreads still worked on this JDK, a pinned-thread stack trace would have printed above while the virtual thread was inside doWorkHoldingMonitor.
|
||||
|
||||
-- with -Djdk.tracePinnedThreads=full --
|
||||
java.version=25.0.4.1
|
||||
jdk.tracePinnedThreads=full
|
||||
Running a virtual thread that holds a monitor across Thread.sleep(200)...
|
||||
Done. If jdk.tracePinnedThreads still worked on this JDK, a pinned-thread stack trace would have printed above while the virtual thread was inside doWorkHoldingMonitor.
|
||||
|
||||
RESULT: no pinned-thread stack trace was printed by either run -- the flag is inert on this JDK, consistent with JEP 491
|
||||
@@ -0,0 +1,4 @@
|
||||
ScopedValue (JEP 506, finalized JDK 25), JDK 25.0.4.1
|
||||
=====================================================
|
||||
ScopedValue.where(CTX, ctx).call(ScopedValueDemo::processRequest) -> handled req-42
|
||||
no --enable-preview flag used on this run
|
||||
Reference in New Issue
Block a user