Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KqJyCidz3ZgRyHABv2GVJh
50 lines
2.9 KiB
Java
50 lines
2.9 KiB
Java
import java.net.URI;
|
|
import java.net.http.HttpClient;
|
|
import java.net.http.HttpOption;
|
|
import java.net.http.HttpRequest;
|
|
import java.net.http.HttpResponse;
|
|
import java.time.Duration;
|
|
import java.util.*;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
/**
|
|
* Bench PROTO WORKLOAD TRIALS LOSS_LABEL
|
|
* PROTO h1 | h2 | h3 (h3 uses HTTP_3_URI_ONLY so a silent fallback cannot flatter the result)
|
|
* WORKLOAD one = fresh client, one 1 MiB download (handshake + one stream)
|
|
* many = fresh client, 10 parallel 100 KiB downloads (handshake + ten streams)
|
|
* Every response is checked: same HTTP version as asked for, exact body length. A trial that throws or
|
|
* exceeds 15 s is counted as failed.
|
|
*/
|
|
public class Bench {
|
|
public static void main(String[] a) throws Exception {
|
|
String proto = a[0], workload = a[1], loss = a[3];
|
|
int trials = Integer.parseInt(a[2]);
|
|
var version = switch (proto) { case "h1" -> HttpClient.Version.HTTP_1_1; case "h2" -> HttpClient.Version.HTTP_2; default -> HttpClient.Version.HTTP_3; };
|
|
int n = workload.equals("one") ? 1 : 10;
|
|
int size = workload.equals("one") ? 1_048_576 : 102_400;
|
|
List<Long> times = new ArrayList<>();
|
|
int failed = 0;
|
|
String firstFailure = null;
|
|
for (int t = -1; t < trials; t++) { // trial -1 is a warm-up and is discarded
|
|
long t0 = System.nanoTime();
|
|
try (var client = Tls.builder().version(version).build()) {
|
|
var rb = HttpRequest.newBuilder(URI.create("https://localhost:4433/bytes/" + size)).timeout(Duration.ofSeconds(15));
|
|
if (version == HttpClient.Version.HTTP_3) rb.setOption(HttpOption.H3_DISCOVERY, HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY);
|
|
var req = rb.build();
|
|
List<CompletableFuture<HttpResponse<byte[]>>> fs = new ArrayList<>();
|
|
for (int i = 0; i < n; i++) fs.add(client.sendAsync(req, HttpResponse.BodyHandlers.ofByteArray()));
|
|
for (var f : fs) {
|
|
var r = f.get();
|
|
if (r.version() != version || r.body().length != size) throw new IllegalStateException(r.version() + " " + r.body().length);
|
|
}
|
|
if (t >= 0) times.add((System.nanoTime() - t0) / 1_000_000);
|
|
} catch (Exception e) {
|
|
if (t >= 0) { failed++; if (firstFailure == null) firstFailure = e.toString(); }
|
|
}
|
|
}
|
|
Collections.sort(times);
|
|
String stats = times.isEmpty() ? "n/a" : "median=%d p90=%d max=%d".formatted(times.get(times.size() / 2), times.get((int) Math.min(times.size() - 1, Math.ceil(times.size() * 0.9) - 1)), times.get(times.size() - 1));
|
|
System.out.printf("%s %-4s loss=%-3s ok=%d failed=%d %s (ms)%s%n", proto, workload, loss, times.size(), failed, stats, firstFailure == null ? "" : " first failure: " + firstFailure);
|
|
}
|
|
}
|