Add shenandoah-jdk28: generational vs SATB Shenandoah, G1 and ZGC on a Spring Boot service (JEP 535 status probe included)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HmvoHH3ZnCEaPNXRpDxDZe
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* Load generator, run with the source launcher: java LoadGen.java open|closed URL RATE_OR_0 CONCURRENCY WARMUP_S MEASURE_S
|
||||
*
|
||||
* open : requests are SCHEDULED at a fixed rate and latency is measured from the scheduled time, so a server
|
||||
* that stalls is charged for the requests that queued behind the stall (no coordinated omission).
|
||||
* closed : CONCURRENCY clients send back to back; the result is the peak throughput.
|
||||
*
|
||||
* Prints one line: mode=... achieved=... p50_ms=... p99_ms=... p999_ms=... max_ms=... errors=... window_start_ms=... window_end_ms=...
|
||||
* (the window is the measured interval in epoch milliseconds, so GC pauses can be cut to exactly that interval).
|
||||
*/
|
||||
public class LoadGen {
|
||||
|
||||
public static void main(String[] a) throws Exception {
|
||||
String mode = a[0];
|
||||
URI uri = URI.create(a[1]);
|
||||
double rate = Double.parseDouble(a[2]);
|
||||
int concurrency = Integer.parseInt(a[3]);
|
||||
int warmup = Integer.parseInt(a[4]);
|
||||
int measure = Integer.parseInt(a[5]);
|
||||
boolean open = mode.equals("open");
|
||||
|
||||
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
|
||||
.connectTimeout(Duration.ofSeconds(5)).build();
|
||||
HttpRequest request = HttpRequest.newBuilder(uri).timeout(Duration.ofSeconds(30)).GET().build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
long epochStartMs = System.currentTimeMillis();
|
||||
long measureFrom = start + warmup * 1_000_000_000L;
|
||||
long end = measureFrom + measure * 1_000_000_000L;
|
||||
int capacity = (int) (open ? rate * measure * 2 + 1000 : 2_000_000);
|
||||
long[] latencies = new long[capacity];
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
AtomicLong errors = new AtomicLong();
|
||||
AtomicLong next = new AtomicLong();
|
||||
long intervalNanos = open ? (long) (1e9 / rate) : 0;
|
||||
|
||||
ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor();
|
||||
for (int t = 0; t < concurrency; t++) {
|
||||
pool.submit(() -> {
|
||||
while (true) {
|
||||
long intended;
|
||||
if (open) {
|
||||
intended = start + next.getAndIncrement() * intervalNanos;
|
||||
if (intended >= end) return;
|
||||
long wait = intended - System.nanoTime();
|
||||
if (wait > 0) LockSupport.parkNanos(wait);
|
||||
} else {
|
||||
intended = System.nanoTime();
|
||||
if (intended >= end) return;
|
||||
}
|
||||
try {
|
||||
HttpResponse<Void> r = client.send(request, HttpResponse.BodyHandlers.discarding());
|
||||
long done = System.nanoTime();
|
||||
if (r.statusCode() != 200) errors.incrementAndGet();
|
||||
if (intended >= measureFrom) {
|
||||
int i = count.getAndIncrement();
|
||||
if (i < latencies.length) latencies[i] = done - intended;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.incrementAndGet();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
pool.shutdown();
|
||||
pool.awaitTermination(measure + warmup + 120, TimeUnit.SECONDS);
|
||||
|
||||
int n = Math.min(count.get(), latencies.length);
|
||||
long[] sorted = Arrays.copyOf(latencies, n);
|
||||
Arrays.sort(sorted);
|
||||
System.out.printf("mode=%s target=%.0f achieved=%.1f requests=%d p50_ms=%.2f p99_ms=%.2f p999_ms=%.2f max_ms=%.2f errors=%d window_start_ms=%d window_end_ms=%d%n",
|
||||
mode, rate, n / (double) measure, n, ms(sorted, 0.50), ms(sorted, 0.99), ms(sorted, 0.999),
|
||||
n == 0 ? 0 : sorted[n - 1] / 1e6, errors.get(),
|
||||
epochStartMs + warmup * 1000L, epochStartMs + (warmup + measure) * 1000L);
|
||||
}
|
||||
|
||||
static double ms(long[] sorted, double q) {
|
||||
if (sorted.length == 0) return 0;
|
||||
return sorted[Math.min(sorted.length - 1, (int) Math.ceil(q * sorted.length) - 1)] / 1e6;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user