diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9d6aa1f..e585924 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,13 +14,14 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '21' distribution: 'temurin' cache: maven - + server-id: github + - name: Build with Maven run: mvn -B package --file pom.xml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 90db424..289dcc4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,10 +13,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '21' distribution: 'temurin' cache: maven diff --git a/README.md b/README.md index 4f2da31..610a284 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,41 @@ `pmqueue` is a simple persistent message queue written in Java (no dependencies). -## Usage +## Quickstart -Just read the [docs](https://elimelt.com/pmqueue/) +```java +import io.github.elimelt.pmqueue.MessageQueue; +import io.github.elimelt.pmqueue.QueueFactory; +import io.github.elimelt.pmqueue.message.Message; + +try (MessageQueue queue = QueueFactory.createQueue("path/to/queue.dat")) { + queue.offer(new Message("Hello, World!".getBytes(), 1)); + Message message = queue.poll(); + System.out.println(new String(message.getData())); +} +``` + +`offer`, `poll`, and `close` throw `IOException`. `QueueFactory` also provides +`createHighThroughputQueue`, `createDurableQueue`, `createLargeMessageQueue`, +`createLowMemoryQueue`, and `createDebugQueue`, each taking a file path. + +## Build and test + +Requires JDK 21+. + +``` +./run_tests.sh +``` + +This downloads a JDK and test dependencies into `target/` on first run, then +compiles and runs the test suite. No Maven install needed. + +Alternatively, with Maven installed: + +``` +mvn test +``` + +## Docs + +Full docs: https://elimelt.com/pmqueue/ diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 0000000..bb04845 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,41 @@ +# Benchmarks + +Before/after benchmarks for the queue library: `main` (before) vs this +branch (after). No Maven, no JMH, no new dependencies. + +Run everything with one command: + +```bash +benchmarks/run_benchmarks.sh +``` + +The driver: + +1. Reuses the Temurin JDK 21 that `run_tests.sh` downloads into `target/jdk/` + (downloads it if missing). +2. Exports library sources for both sides with `git archive` into a scratch + dir (`/tmp/pmqueue-bench`), so the working tree is never modified. +3. Compiles the same harness (`src/QueueBench.java`) once against each + side's classes. The harness only uses APIs that exist on both sides. +4. Runs each scenario in a fresh JVM, one JSON line per run. Trials are + interleaved: each trial runs both sides back to back, and the side that + goes first alternates per trial to reduce machine-noise bias. +5. Aggregates medians (`aggregate.py`, stdlib only) and renders plots + (`make_plots.py`, matplotlib from a scratch venv at `/tmp/benchvenv`). + +Scenarios: `offer()` and `poll()` throughput across 64 B / 1 KiB / 8 KiB / +64 KiB, checksums on vs off at 1 KiB, single-message round-trip latency +percentiles, and open+close cost on a populated file. Memory is captured per +run: bytes allocated per operation (per-thread allocation counter), GC +count/time deltas, settled heap after a full produce/consume cycle, and peak +RSS (`/usr/bin/time -l` on macOS). + +Knobs (environment variables): `TRIALS` (default 5), `BEFORE_REF` (default +`main`), `AFTER_REF` (default `HEAD`). + +Outputs in `results/`: `raw.jsonl` and `raw.csv` (per-trial data), +`medians.json`, `summary.md`, `env.json`, and the `*.png` plots. + +Note: `poll()` persists its read position with an fsync per message on both +sides, so poll throughput and round-trip latency are fsync-bound; op counts +are sized accordingly. diff --git a/benchmarks/aggregate.py b/benchmarks/aggregate.py new file mode 100644 index 0000000..d4c0796 --- /dev/null +++ b/benchmarks/aggregate.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Aggregate raw benchmark output into CSV, medians JSON, and summary.md. + +Reads results/raw.jsonl (one JSON object per trial run) and writes: + results/raw.csv flat per-trial table + results/medians.json per-group medians/min/max for plotting + results/summary.md median comparison table, main vs this branch + +Uses only the Python standard library. +""" +import csv +import json +import statistics +import sys +from pathlib import Path + +SIDES = ("main", "branch") + +# metric -> (label, unit, higher_is_better) +METRICS = { + "msgs_per_sec": ("throughput", "msgs/s", True), + "mib_per_sec": ("throughput", "MiB/s", True), + "alloc_bytes_per_op": ("allocation", "B/op", False), + "gc_count": ("gc collections", "count", False), + "gc_time_ms": ("gc time", "ms", False), + "heap_after_cycle_bytes": ("settled heap", "bytes", False), + "peak_rss_bytes": ("peak RSS", "bytes", False), + "p50_us": ("latency p50", "us", False), + "p95_us": ("latency p95", "us", False), + "p99_us": ("latency p99", "us", False), + "mean_us": ("latency mean", "us", False), +} + + +def fmt_size(n): + if n >= 1024 * 1024: + return f"{n // (1024 * 1024)} MiB" + if n >= 1024: + return f"{n // 1024} KiB" + return f"{n} B" + + +def fmt_val(metric, v): + if v is None: + return "-" + if metric in ("msgs_per_sec",): + return f"{v:,.0f}" + if metric in ("mib_per_sec",): + return f"{v:,.1f}" + if metric in ("alloc_bytes_per_op",): + return f"{v:,.0f}" + if metric.endswith("_us"): + return f"{v:,.1f}" + if metric.endswith("_bytes"): + return f"{v / (1024 * 1024):,.1f} MiB" + return f"{v:,.0f}" + + +def main(): + results = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parent / "results" + rows = [json.loads(line) for line in (results / "raw.jsonl").read_text().splitlines() if line.strip()] + env = json.loads((results / "env.json").read_text()) if (results / "env.json").exists() else {} + + # raw.csv + fieldnames = [] + for r in rows: + for k in r: + if k not in fieldnames: + fieldnames.append(k) + with open(results / "raw.csv", "w", newline="") as f: + w = csv.DictWriter(f, fieldnames=fieldnames) + w.writeheader() + w.writerows(rows) + + # group by (scenario, size, checksum) + groups = {} + for r in rows: + key = (r["scenario"], r["size"], r["checksum"]) + groups.setdefault(key, {s: [] for s in SIDES})[r["side"]].append(r) + + out_groups = [] + for key in sorted(groups, key=lambda k: (k[0], k[1], not k[2])): + scenario, size, checksum = key + metrics = {} + for metric in METRICS: + stats = {} + for side in SIDES: + vals = [r[metric] for r in groups[key][side] if r.get(metric) is not None] + if not vals: + continue + stats[side] = { + "median": statistics.median(vals), + "min": min(vals), + "max": max(vals), + "n": len(vals), + } + if len(stats) == len(SIDES): + metrics[metric] = stats + out_groups.append({ + "scenario": scenario, + "size": size, + "checksum": checksum, + "metrics": metrics, + }) + + (results / "medians.json").write_text(json.dumps({"env": env, "groups": out_groups}, indent=2)) + + # summary.md + trials = env.get("trials", "?") + lines = [] + lines.append("# Benchmark summary: main vs this branch") + lines.append("") + lines.append(f"- JVM: {env.get('jvm', 'unknown')}, flags `{env.get('jvm_flags', '')}`") + lines.append(f"- OS: {env.get('os', 'unknown')}, date {env.get('date', '')}") + lines.append(f"- Trials: {trials} per side, interleaved (fresh JVM per trial). Values are medians.") + lines.append(f"- Before = `main` ({env.get('before_ref', '')}), after = this branch ({env.get('after_ref', '')}).") + lines.append("- Delta = (after - before) / before. Positive throughput delta is better;") + lines.append(" positive latency/memory delta is worse. Deltas within the run-to-run noise") + lines.append(" band (max spread across trials of either side) are marked `~` (equivalent).") + lines.append("") + + headline = { + "offer": ["msgs_per_sec", "mib_per_sec", "alloc_bytes_per_op", "gc_time_ms", "peak_rss_bytes"], + "poll": ["msgs_per_sec", "mib_per_sec", "alloc_bytes_per_op", "gc_time_ms", + "heap_after_cycle_bytes", "peak_rss_bytes"], + "latency": ["p50_us", "p95_us", "p99_us", "alloc_bytes_per_op"], + "openclose": ["p50_us", "p95_us", "mean_us"], + } + scenario_title = { + "offer": "offer() throughput", + "poll": "poll() throughput", + "latency": "Round-trip latency (offer+poll)", + "openclose": "Open+close on populated file (5,000 x 1 KiB messages)", + } + + for scenario in ("offer", "poll", "latency", "openclose"): + sgroups = [g for g in out_groups if g["scenario"] == scenario] + if not sgroups: + continue + lines.append(f"## {scenario_title[scenario]}") + lines.append("") + lines.append("| Case | Metric | main | this branch | delta |") + lines.append("|---|---|---:|---:|---:|") + for g in sgroups: + case = f"{fmt_size(g['size'])}, checksum {'on' if g['checksum'] else 'off'}" + for metric in headline[scenario]: + if metric not in g["metrics"]: + continue + st = g["metrics"][metric] + m, b = st["main"]["median"], st["branch"]["median"] + if metric in ("gc_count", "gc_time_ms") and max(m, b) < 5: + # both sides negligible; percentage deltas would mislead + continue + unit = METRICS[metric][1] + if m == 0: + delta = "-" + else: + pct = (b - m) / m * 100 + noise = max( + (st[s]["max"] - st[s]["min"]) / st[s]["median"] * 100 + for s in SIDES if st[s]["median"] + ) + mark = " ~" if abs(pct) <= noise else "" + delta = f"{pct:+.1f}%{mark}" + lines.append( + f"| {case} | {metric} ({unit}) | {fmt_val(metric, m)} | {fmt_val(metric, b)} | {delta} |") + lines.append("") + + lines.append("`~` = within run-to-run noise; treat as equivalent.") + lines.append("") + (results / "summary.md").write_text("\n".join(lines)) + print(f"wrote {results / 'raw.csv'}, {results / 'medians.json'}, {results / 'summary.md'}") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/make_plots.py b/benchmarks/make_plots.py new file mode 100644 index 0000000..df73af7 --- /dev/null +++ b/benchmarks/make_plots.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +"""Render comparison plots (PNG) from results/medians.json. + +Requires matplotlib (run_benchmarks.sh installs it into a scratch venv). +Two series only: main (before) and this branch (after). Bars show medians +across trials; whiskers show the min-max spread across trials. +""" +import json +import sys +from pathlib import Path + +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +from matplotlib.ticker import FuncFormatter + +SURFACE = "#fcfcfb" +INK = "#0b0b0b" +SECONDARY = "#52514e" +MUTED = "#898781" +GRID = "#e1e0d9" +BASELINE = "#c3c2b7" +C_MAIN = "#2a78d6" # series 1: main (before) +C_BRANCH = "#eb6834" # series 2: this branch (after) + +SIDES = ["main", "branch"] +SIDE_LABEL = {"main": "main", "branch": "this branch"} +SIDE_COLOR = {"main": C_MAIN, "branch": C_BRANCH} + + +def fmt_size(n): + if n >= 1024 * 1024: + return f"{n // (1024 * 1024)} MiB" + if n >= 1024: + return f"{n // 1024} KiB" + return f"{n} B" + + +def fmt_val(v): + if v >= 10000: + return f"{v / 1000:,.1f}k" + if v >= 100: + return f"{v:,.0f}" + if v >= 10: + return f"{v:,.1f}" + if v >= 0.1: + return f"{v:,.2f}" + return f"{v:,.3f}" + + +def style_axis(ax): + ax.set_facecolor(SURFACE) + for side in ("top", "right", "left"): + ax.spines[side].set_visible(False) + ax.spines["bottom"].set_color(BASELINE) + ax.spines["bottom"].set_linewidth(1) + ax.tick_params(colors=MUTED, labelsize=8, length=0) + ax.yaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:,.10g}")) + ax.yaxis.grid(True, color=GRID, linewidth=0.8) + ax.set_axisbelow(True) + + +def paired_bars(ax, stats_by_side, label_values=True): + """Two thin bars (main, branch) with min-max whiskers and tip labels.""" + xs = [0, 1] + width = 0.55 + tops = [] + for x, side in zip(xs, SIDES): + st = stats_by_side[side] + ax.bar(x, st["median"], width=width, color=SIDE_COLOR[side], zorder=3) + ax.vlines(x, st["min"], st["max"], color=SECONDARY, linewidth=1, zorder=4) + tops.append(max(st["max"], st["median"])) + top = max(tops) if tops else 1 + ax.set_ylim(0, top * 1.28) + if label_values: + for x, side in zip(xs, SIDES): + st = stats_by_side[side] + ax.annotate(fmt_val(st["median"]), (x, max(st["max"], st["median"])), + xytext=(0, 3), textcoords="offset points", + ha="center", va="bottom", fontsize=8, color=INK) + ax.set_xticks(xs) + ax.set_xticklabels([], fontsize=8) + ax.set_xlim(-0.75, 1.75) + style_axis(ax) + + +def grouped_bars(ax, group_labels, stats_list, unit): + """Grouped bars: one group per label, two bars (main, branch) per group.""" + n = len(group_labels) + width = 0.32 + gap = 0.04 + for gi, st in enumerate(stats_list): + for si, side in enumerate(SIDES): + s = st[side] + x = gi + (si - 0.5) * (width + gap) + ax.bar(x, s["median"], width=width, color=SIDE_COLOR[side], zorder=3) + ax.vlines(x, s["min"], s["max"], color=SECONDARY, linewidth=1, zorder=4) + ax.annotate(fmt_val(s["median"]), (x, max(s["max"], s["median"])), + xytext=(0, 3), textcoords="offset points", + ha="center", va="bottom", fontsize=8, color=INK) + tops = [max(st[s]["max"], st[s]["median"]) for st in stats_list for s in SIDES] + ax.set_ylim(0, max(tops) * 1.22) + ax.set_xticks(range(n)) + ax.set_xticklabels(group_labels, fontsize=9, color=SECONDARY) + ax.set_ylabel(unit, fontsize=9, color=SECONDARY) + style_axis(ax) + + +def legend(fig): + handles = [plt.Rectangle((0, 0), 1, 1, color=SIDE_COLOR[s]) for s in SIDES] + fig.legend(handles, [SIDE_LABEL[s] for s in SIDES], loc="upper right", + frameon=False, fontsize=9, labelcolor=SECONDARY, + bbox_to_anchor=(0.99, 1.0)) + + +def titled(fig, title, subtitle, sub_y=0.885): + fig.suptitle(title, x=0.02, y=0.975, ha="left", va="top", + fontsize=13, color=INK, fontweight="bold") + fig.text(0.02, sub_y, subtitle, ha="left", va="top", + fontsize=9, color=SECONDARY) + + +def new_fig(w, h): + fig = plt.figure(figsize=(w, h), dpi=160) + fig.patch.set_facecolor(SURFACE) + return fig + + +def group_key(g): + return (g["scenario"], g["size"], g["checksum"]) + + +def main(): + results = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parent / "results" + data = json.loads((results / "medians.json").read_text()) + groups = {group_key(g): g["metrics"] for g in data["groups"]} + trials = data.get("env", {}).get("trials", "?") + sizes = sorted({g["size"] for g in data["groups"] if g["scenario"] == "offer"}) + note = f"median of {trials} interleaved trials per side, whiskers = min-max across trials" + + # --- throughput: small multiples, offer row + poll row, per-size scale --- + fig = new_fig(9, 5.2) + axes = fig.subplots(2, len(sizes)) + for row, scenario in enumerate(("offer", "poll")): + for col, size in enumerate(sizes): + ax = axes[row][col] + key = (scenario, size, True) + if key not in groups: + ax.axis("off") + continue + paired_bars(ax, groups[key]["mib_per_sec"]) + if row == 0: + ax.set_title(fmt_size(size), fontsize=9, color=SECONDARY, pad=8) + if col == 0: + ax.set_ylabel(f"{scenario}()\nMiB/s", fontsize=9, color=SECONDARY) + titled(fig, "Throughput by message size (payload MiB/s)", + f"checksums on; {note};\n" + "each panel has its own scale. poll() fsyncs its read position per message on both sides.") + legend(fig) + fig.tight_layout(rect=(0.01, 0.02, 0.99, 0.82)) + fig.savefig(results / "throughput.png", facecolor=SURFACE) + plt.close(fig) + + # --- latency percentiles ------------------------------------------------- + fig = new_fig(7, 4.2) + ax = fig.subplots(1, 1) + lat = groups[("latency", 1024, True)] + grouped_bars(ax, ["p50", "p95", "p99"], + [lat["p50_us"], lat["p95_us"], lat["p99_us"]], + "microseconds") + titled(fig, "Round-trip latency, 1 KiB message", + f"one offer() + one poll() per round trip; checksums on; lower is better;\n{note}", + sub_y=0.87) + legend(fig) + fig.tight_layout(rect=(0.01, 0.02, 0.99, 0.78)) + fig.savefig(results / "latency.png", facecolor=SURFACE) + plt.close(fig) + + # --- checksums on vs off -------------------------------------------------- + fig = new_fig(8, 4.2) + ax_offer, ax_poll = fig.subplots(1, 2) + for ax, scenario in ((ax_offer, "offer"), (ax_poll, "poll")): + grouped_bars(ax, ["checksums on", "checksums off"], + [groups[(scenario, 1024, True)]["msgs_per_sec"], + groups[(scenario, 1024, False)]["msgs_per_sec"]], + "messages/s") + ax.set_title(f"{scenario}()", fontsize=10, color=SECONDARY, pad=8) + titled(fig, "Checksum cost at 1 KiB (messages/s)", + f"higher is better;\n{note}", sub_y=0.87) + legend(fig) + fig.tight_layout(rect=(0.01, 0.02, 0.99, 0.76)) + fig.savefig(results / "checksum.png", facecolor=SURFACE) + plt.close(fig) + + # --- open/close on populated file ----------------------------------------- + fig = new_fig(7, 4.2) + ax = fig.subplots(1, 1) + oc = groups[("openclose", 1024, True)] + grouped_bars(ax, ["p50", "p95", "mean"], + [oc["p50_us"], oc["p95_us"], oc["mean_us"]], + "microseconds") + titled(fig, "Open + close on a populated queue file", + f"5,000 x 1 KiB messages on disk; 100 cycles/trial; lower is better;\n{note}", sub_y=0.87) + legend(fig) + fig.tight_layout(rect=(0.01, 0.02, 0.99, 0.78)) + fig.savefig(results / "open_close.png", facecolor=SURFACE) + plt.close(fig) + + # --- memory: allocation per operation -------------------------------------- + fig = new_fig(9, 5.2) + axes = fig.subplots(2, len(sizes)) + for row, scenario in enumerate(("offer", "poll")): + for col, size in enumerate(sizes): + ax = axes[row][col] + key = (scenario, size, True) + if key not in groups or "alloc_bytes_per_op" not in groups[key]: + ax.axis("off") + continue + st = groups[key]["alloc_bytes_per_op"] + kib = {s: {k: v / 1024 if k != "n" else v for k, v in st[s].items()} for s in SIDES} + paired_bars(ax, kib) + if row == 0: + ax.set_title(fmt_size(size), fontsize=9, color=SECONDARY, pad=8) + if col == 0: + ax.set_ylabel(f"{scenario}()\nKiB alloc/op", fontsize=9, color=SECONDARY) + titled(fig, "Heap allocation per operation (KiB, lower is better)", + f"per-thread allocation counter over the measured window; checksums on;\n" + f"{note}; each panel has its own scale") + legend(fig) + fig.tight_layout(rect=(0.01, 0.02, 0.99, 0.82)) + fig.savefig(results / "memory.png", facecolor=SURFACE) + plt.close(fig) + + print(f"wrote plots to {results}") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/results/checksum.png b/benchmarks/results/checksum.png new file mode 100644 index 0000000..011fbc0 Binary files /dev/null and b/benchmarks/results/checksum.png differ diff --git a/benchmarks/results/env.json b/benchmarks/results/env.json new file mode 100644 index 0000000..8e97335 --- /dev/null +++ b/benchmarks/results/env.json @@ -0,0 +1,9 @@ +{ + "date": "2026-08-15T06:30:57Z", + "os": "Darwin arm64", + "jvm": "openjdk version \"21.0.12\" 2026-07-21 LTS", + "jvm_flags": "-Xms512m -Xmx512m", + "trials": 5, + "before_ref": "c04e37b", + "after_ref": "de5f041" +} diff --git a/benchmarks/results/latency.png b/benchmarks/results/latency.png new file mode 100644 index 0000000..8490fbc Binary files /dev/null and b/benchmarks/results/latency.png differ diff --git a/benchmarks/results/medians.json b/benchmarks/results/medians.json new file mode 100644 index 0000000..51c4de8 --- /dev/null +++ b/benchmarks/results/medians.json @@ -0,0 +1,1189 @@ +{ + "env": { + "date": "2026-08-15T06:30:57Z", + "os": "Darwin arm64", + "jvm": "openjdk version \"21.0.12\" 2026-07-21 LTS", + "jvm_flags": "-Xms512m -Xmx512m", + "trials": 5, + "before_ref": "c04e37b", + "after_ref": "de5f041" + }, + "groups": [ + { + "scenario": "latency", + "size": 1024, + "checksum": true, + "metrics": { + "alloc_bytes_per_op": { + "main": { + "median": 8785.98, + "min": 8785.98, + "max": 8785.98, + "n": 5 + }, + "branch": { + "median": 8953.98, + "min": 8953.98, + "max": 8954.7, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 66584576, + "min": 66338816, + "max": 66813952, + "n": 5 + }, + "branch": { + "median": 66650112, + "min": 66551808, + "max": 66797568, + "n": 5 + } + }, + "p50_us": { + "main": { + "median": 8692.042, + "min": 8403.167, + "max": 8754.875, + "n": 5 + }, + "branch": { + "median": 8576.792, + "min": 8338.5, + "max": 9415.083, + "n": 5 + } + }, + "p95_us": { + "main": { + "median": 10375.917, + "min": 10149.792, + "max": 11181.167, + "n": 5 + }, + "branch": { + "median": 12470.083, + "min": 9842.084, + "max": 13484.583, + "n": 5 + } + }, + "p99_us": { + "main": { + "median": 11827.334, + "min": 10600.541, + "max": 18663.25, + "n": 5 + }, + "branch": { + "median": 17035.958, + "min": 11719.167, + "max": 22081.167, + "n": 5 + } + }, + "mean_us": { + "main": { + "median": 8835.05769, + "min": 8619.909305, + "max": 9304.376695, + "n": 5 + }, + "branch": { + "median": 9136.098970000001, + "min": 8573.51821, + "max": 9733.8479225, + "n": 5 + } + } + } + }, + { + "scenario": "offer", + "size": 64, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 13444.174124466888, + "min": 10909.10568597043, + "max": 14588.887341588255, + "n": 5 + }, + "branch": { + "median": 11361.604939659348, + "min": 10356.721745439028, + "max": 14149.166905584547, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 0.8205672683390435, + "min": 0.6658389700909686, + "max": 0.8904350184074863, + "n": 5 + }, + "branch": { + "median": 0.6934573327428801, + "min": 0.6321241299706438, + "max": 0.8635966128896818, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 297.51952, + "min": 297.51952, + "max": 297.51952, + "n": 5 + }, + "branch": { + "median": 298.26976, + "min": 298.26976, + "max": 298.27248, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 92110848, + "min": 91963392, + "max": 92487680, + "n": 5 + }, + "branch": { + "median": 92946432, + "min": 92749824, + "max": 94240768, + "n": 5 + } + } + } + }, + { + "scenario": "offer", + "size": 1024, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 12049.902019198056, + "min": 10209.989449266446, + "max": 12659.061234162631, + "n": 5 + }, + "branch": { + "median": 12592.118825781143, + "min": 10486.477385924625, + "max": 13371.724445665135, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 11.767482440623102, + "min": 9.970692821549264, + "max": 12.362364486486944, + "n": 5 + }, + "branch": { + "median": 12.296991040801897, + "min": 10.240700572192017, + "max": 13.058324653969859, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 3177.51952, + "min": 3177.51952, + "max": 3177.51952, + "n": 5 + }, + "branch": { + "median": 3178.26976, + "min": 3178.26976, + "max": 3178.26976, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 1, + "min": 0, + "max": 10, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 2, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 167870464, + "min": 167542784, + "max": 168329216, + "n": 5 + }, + "branch": { + "median": 179421184, + "min": 178683904, + "max": 181190656, + "n": 5 + } + } + } + }, + { + "scenario": "offer", + "size": 1024, + "checksum": false, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 13482.907619316109, + "min": 12606.106916099456, + "max": 13716.236241457742, + "n": 5 + }, + "branch": { + "median": 12916.534139904896, + "min": 12458.916722108846, + "max": 13375.826847836966, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 13.166901971988388, + "min": 12.310651285253375, + "max": 13.394761954548576, + "n": 5 + }, + "branch": { + "median": 12.613802871000875, + "min": 12.16691086143442, + "max": 13.062330906090788, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 3177.51952, + "min": 3177.51952, + "max": 3177.51952, + "n": 5 + }, + "branch": { + "median": 3178.26976, + "min": 3178.26976, + "max": 3178.26976, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 2, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 167870464, + "min": 167051264, + "max": 168361984, + "n": 5 + }, + "branch": { + "median": 179945472, + "min": 179421184, + "max": 181452800, + "n": 5 + } + } + } + }, + { + "scenario": "offer", + "size": 8192, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 11784.222634604546, + "min": 11210.88224554653, + "max": 12342.070358581193, + "n": 5 + }, + "branch": { + "median": 12020.792374186263, + "min": 11568.806852551363, + "max": 12368.790968667035, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 92.06423933284802, + "min": 87.58501754333227, + "max": 96.42242467641557, + "n": 5 + }, + "branch": { + "median": 93.91244042333018, + "min": 90.38130353555752, + "max": 96.63117944271121, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 24681.7176, + "min": 24681.7176, + "max": 24681.7176, + "n": 5 + }, + "branch": { + "median": 24682.436, + "min": 24682.4224, + "max": 24682.436, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 278773760, + "min": 277430272, + "max": 279183360, + "n": 5 + }, + "branch": { + "median": 290160640, + "min": 289538048, + "max": 291602432, + "n": 5 + } + } + } + }, + { + "scenario": "offer", + "size": 65536, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 7129.046753486289, + "min": 6601.3054906771, + "max": 7235.37411449693, + "n": 5 + }, + "branch": { + "median": 6785.079489428333, + "min": 6734.243861017841, + "max": 7448.173523137423, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 445.5654220928931, + "min": 412.5815931673188, + "max": 452.2108821560581, + "n": 5 + }, + "branch": { + "median": 424.0674680892708, + "min": 420.89024131361504, + "max": 465.51084519608895, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 196714.132, + "min": 196714.132, + "max": 196714.132, + "n": 5 + }, + "branch": { + "median": 196714.888, + "min": 196714.888, + "max": 196714.888, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 0, + "max": 1, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 378224640, + "min": 378093568, + "max": 378421248, + "n": 5 + }, + "branch": { + "median": 378372096, + "min": 378306560, + "max": 378617856, + "n": 5 + } + } + } + }, + { + "scenario": "openclose", + "size": 1024, + "checksum": true, + "metrics": { + "peak_rss_bytes": { + "main": { + "median": 621051904, + "min": 620085248, + "max": 628375552, + "n": 5 + }, + "branch": { + "median": 631881728, + "min": 616972288, + "max": 648626176, + "n": 5 + } + }, + "p50_us": { + "main": { + "median": 8634.791, + "min": 8323.042, + "max": 9678.083, + "n": 5 + }, + "branch": { + "median": 9364.5, + "min": 6410.375, + "max": 9446.375, + "n": 5 + } + }, + "p95_us": { + "main": { + "median": 11391.667, + "min": 10462.209, + "max": 12694.958, + "n": 5 + }, + "branch": { + "median": 12437.958, + "min": 10846.375, + "max": 13790.833, + "n": 5 + } + }, + "p99_us": { + "main": { + "median": 15246.458, + "min": 11968.708, + "max": 20844.75, + "n": 5 + }, + "branch": { + "median": 15472.083, + "min": 11850.75, + "max": 17935.417, + "n": 5 + } + }, + "mean_us": { + "main": { + "median": 8991.40748, + "min": 7624.9687300000005, + "max": 9867.78131, + "n": 5 + }, + "branch": { + "median": 9700.30919, + "min": 7420.27625, + "max": 9910.130019999999, + "n": 5 + } + } + } + }, + { + "scenario": "poll", + "size": 64, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 214.39103896773753, + "min": 199.5403388753667, + "max": 236.98161884919048, + "n": 5 + }, + "branch": { + "median": 200.5286090278919, + "min": 193.00518024295397, + "max": 234.29472659972632, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 0.013085390561995699, + "min": 0.012178975761435956, + "max": 0.014464210134838286, + "n": 5 + }, + "branch": { + "median": 0.012239294984612542, + "min": 0.011780101333188108, + "max": 0.014300215246565327, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 712.768, + "min": 712.768, + "max": 712.768, + "n": 5 + }, + "branch": { + "median": 816.8426666666667, + "min": 816.8426666666667, + "max": 816.8426666666667, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "heap_after_cycle_bytes": { + "main": { + "median": 1341024, + "min": 1341024, + "max": 1341024, + "n": 5 + }, + "branch": { + "median": 1362544, + "min": 1362544, + "max": 1362544, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 102236160, + "min": 101957632, + "max": 102531072, + "n": 5 + }, + "branch": { + "median": 102809600, + "min": 102514688, + "max": 102907904, + "n": 5 + } + } + } + }, + { + "scenario": "poll", + "size": 1024, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 217.39718037306372, + "min": 203.11170517523547, + "max": 220.22550820722594, + "n": 5 + }, + "branch": { + "median": 220.3139120744802, + "min": 200.18222700226062, + "max": 231.0025670663851, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 0.21230193395807004, + "min": 0.1983512745851909, + "max": 0.21506397285861908, + "n": 5 + }, + "branch": { + "median": 0.21515030476023458, + "min": 0.19549045605689513, + "max": 0.2255884444007667, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 5512.768, + "min": 5512.768, + "max": 5512.768, + "n": 5 + }, + "branch": { + "median": 5632.858666666667, + "min": 5632.858666666667, + "max": 5632.858666666667, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "heap_after_cycle_bytes": { + "main": { + "median": 1341992, + "min": 1341992, + "max": 1341992, + "n": 5 + }, + "branch": { + "median": 1363512, + "min": 1363512, + "max": 1363512, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 116834304, + "min": 116424704, + "max": 116883456, + "n": 5 + }, + "branch": { + "median": 117358592, + "min": 116867072, + "max": 117735424, + "n": 5 + } + } + } + }, + { + "scenario": "poll", + "size": 1024, + "checksum": false, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 207.79549774725936, + "min": 202.9549713987885, + "max": 228.67318449718158, + "n": 5 + }, + "branch": { + "median": 201.22361958146064, + "min": 190.94533205190396, + "max": 223.51241172446402, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 0.20292529076880797, + "min": 0.1981982142566294, + "max": 0.2233136567355289, + "n": 5 + }, + "branch": { + "median": 0.19650744099752016, + "min": 0.18647005083193746, + "max": 0.2182738395746719, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 5512.768, + "min": 5512.768, + "max": 5512.768, + "n": 5 + }, + "branch": { + "median": 5632.858666666667, + "min": 5632.858666666667, + "max": 5632.858666666667, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + }, + "branch": { + "median": 0, + "min": 0, + "max": 0, + "n": 5 + } + }, + "heap_after_cycle_bytes": { + "main": { + "median": 1341560, + "min": 1341560, + "max": 1341560, + "n": 5 + }, + "branch": { + "median": 1363080, + "min": 1363080, + "max": 1363080, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 116621312, + "min": 116375552, + "max": 117112832, + "n": 5 + }, + "branch": { + "median": 117178368, + "min": 116736000, + "max": 117342208, + "n": 5 + } + } + } + }, + { + "scenario": "poll", + "size": 8192, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 202.28146295493283, + "min": 189.7634690077828, + "max": 229.1654487064153, + "n": 5 + }, + "branch": { + "median": 201.26064523955966, + "min": 195.3829595472984, + "max": 221.624052076364, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 1.5803239293354128, + "min": 1.482527101623303, + "max": 1.7903550680188696, + "n": 5 + }, + "branch": { + "median": 1.5723487909340599, + "min": 1.5264293714632688, + "max": 1.7314379068465937, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 41352.768, + "min": 41352.768, + "max": 41352.768, + "n": 5 + }, + "branch": { + "median": 41472.85866666667, + "min": 41472.85866666667, + "max": 41472.85866666667, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 1, + "min": 0, + "max": 3, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 10, + "n": 5 + } + }, + "heap_after_cycle_bytes": { + "main": { + "median": 1348584, + "min": 1348584, + "max": 1348584, + "n": 5 + }, + "branch": { + "median": 1369336, + "min": 1369336, + "max": 1369336, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 164954112, + "min": 163921920, + "max": 165691392, + "n": 5 + }, + "branch": { + "median": 156155904, + "min": 154157056, + "max": 156368896, + "n": 5 + } + } + } + }, + { + "scenario": "poll", + "size": 65536, + "checksum": true, + "metrics": { + "msgs_per_sec": { + "main": { + "median": 208.0111823525056, + "min": 167.56035681140182, + "max": 236.3003662692599, + "n": 5 + }, + "branch": { + "median": 216.7565037704286, + "min": 166.54478246367478, + "max": 227.74646852496917, + "n": 5 + } + }, + "mib_per_sec": { + "main": { + "median": 13.0006988970316, + "min": 10.472522300712614, + "max": 14.768772891828744, + "n": 5 + }, + "branch": { + "median": 13.547281485651787, + "min": 10.409048903979674, + "max": 14.234154282810573, + "n": 5 + } + }, + "alloc_bytes_per_op": { + "main": { + "median": 328072.99, + "min": 328072.99, + "max": 328072.99, + "n": 5 + }, + "branch": { + "median": 328192.99, + "min": 328192.99, + "max": 328192.99, + "n": 5 + } + }, + "gc_count": { + "main": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 1, + "n": 5 + } + }, + "gc_time_ms": { + "main": { + "median": 2, + "min": 1, + "max": 4, + "n": 5 + }, + "branch": { + "median": 1, + "min": 1, + "max": 3, + "n": 5 + } + }, + "heap_after_cycle_bytes": { + "main": { + "median": 1405568, + "min": 1405568, + "max": 1405568, + "n": 5 + }, + "branch": { + "median": 1426184, + "min": 1426184, + "max": 1426184, + "n": 5 + } + }, + "peak_rss_bytes": { + "main": { + "median": 397918208, + "min": 396754944, + "max": 398360576, + "n": 5 + }, + "branch": { + "median": 398622720, + "min": 398245888, + "max": 399687680, + "n": 5 + } + } + } + } + ] +} \ No newline at end of file diff --git a/benchmarks/results/memory.png b/benchmarks/results/memory.png new file mode 100644 index 0000000..dbd3778 Binary files /dev/null and b/benchmarks/results/memory.png differ diff --git a/benchmarks/results/open_close.png b/benchmarks/results/open_close.png new file mode 100644 index 0000000..3fd070e Binary files /dev/null and b/benchmarks/results/open_close.png differ diff --git a/benchmarks/results/raw.csv b/benchmarks/results/raw.csv new file mode 100644 index 0000000..2795cfc --- /dev/null +++ b/benchmarks/results/raw.csv @@ -0,0 +1,121 @@ +scenario,side,trial,size,checksum,ops,elapsed_ns,msgs_per_sec,mib_per_sec,alloc_bytes_per_op,gc_count,gc_time_ms,sink,peak_rss_bytes,heap_after_cycle_bytes,p50_us,p95_us,p99_us,max_us,mean_us,populated_msgs +offer,main,1,64,True,50000,3719083042,13444.174124466888,0.8205672683390435,297.51952,0,0,0,92078080,,,,,,, +offer,main,1,1024,True,50000,4017336666,12446.056717916108,12.15435226358995,3177.51952,1,10,0,167804928,,,,,,, +offer,main,1,8192,True,10000,837004041,11947.373620864037,93.33885641300029,24681.7176,1,1,0,279183360,,,,,,, +offer,main,1,65536,True,2000,280542416,7129.046753486289,445.5654220928931,196714.132,1,1,0,378224640,,,,,,, +poll,main,1,64,True,1500,6996561084,214.39103896773753,0.013085390561995699,712.768,0,0,400,101957632,1341024,,,,,, +poll,main,1,1024,True,1500,6883683125,217.90660214331118,0.21279941615557732,5512.768,0,0,400,116834304,1341992,,,,,, +poll,main,1,8192,True,1500,7575483917,198.00715260366118,1.546930879716103,41352.768,1,1,400,164954112,1348584,,,,,, +poll,main,1,65536,True,800,3385521625,236.3003662692599,14.768772891828744,328072.99,1,1,0,398147584,1405568,,,,,, +offer,main,1,1024,False,50000,3645314875,13716.236241457742,13.394761954548576,3177.51952,1,1,0,168361984,,,,,,, +poll,main,1,1024,False,1500,6559579792,228.67318449718158,0.2233136567355289,5512.768,0,0,400,117112832,1341560,,,,,, +latency,main,1,1024,True,400,,,,8785.98,0,0,440,66420736,,8403.167,10149.792,18061.75,22383.291,8710.1485675, +openclose,main,1,1024,True,100,,,,,,,0,620085248,,9678.083,12694.958,17816.833,,9867.78131,5000 +offer,branch,1,64,True,50000,4827782500,10356.721745439028,0.6321241299706438,298.27248,0,0,0,92749824,,,,,,, +offer,branch,1,1024,True,50000,3739233500,13371.724445665135,13.058324653969859,3178.26976,1,2,0,178978816,,,,,,, +offer,branch,1,8192,True,10000,817242417,12236.271382864357,95.59587017862779,24682.436,1,1,0,291602432,,,,,,, +offer,branch,1,65536,True,2000,295911458,6758.778499209044,422.42365620056523,196714.888,1,1,0,378306560,,,,,,, +poll,branch,1,64,True,1500,7548540917,198.71389934733793,0.012128533895711543,816.8426666666667,0,0,400,102907904,1362544,,,,,, +poll,branch,1,1024,True,1500,6493434333,231.0025670663851,0.2255884444007667,5632.858666666667,0,0,400,117358592,1363512,,,,,, +poll,branch,1,8192,True,1500,7661396958,195.7867485816286,1.5295839732939733,41472.85866666667,1,1,400,156352512,1369336,,,,,, +poll,branch,1,65536,True,800,3742801500,213.7436356162623,13.358977226016394,328192.99,1,1,0,398622720,1426184,,,,,, +offer,branch,1,1024,False,50000,3738086667,13375.826847836966,13.062330906090788,3178.26976,1,1,0,181452800,,,,,,, +poll,branch,1,1024,False,1500,6711036709,223.51241172446402,0.2182738395746719,5632.858666666667,0,0,400,117342208,1363080,,,,,, +latency,branch,1,1024,True,400,,,,8954.7,0,0,440,66797568,,8657.916,12844.792,17035.958,27945.333,9268.424565, +openclose,branch,1,1024,True,100,,,,,,,0,619413504,,9446.375,12437.958,15472.083,,9700.30919,5000 +offer,branch,2,64,True,50000,3733240625,13393.189730436945,0.8174554278831143,298.27248,0,0,0,92946432,,,,,,, +offer,branch,2,1024,True,50000,3912723708,12778.822051188901,12.479318409364161,3178.26976,1,1,0,178683904,,,,,,, +offer,branch,2,8192,True,10000,808486458,12368.790968667035,96.63117944271121,24682.436,1,1,0,289538048,,,,,,, +offer,branch,2,65536,True,2000,268522208,7448.173523137423,465.51084519608895,196714.888,1,0,0,378617856,,,,,,, +poll,branch,2,64,True,1500,7771812125,193.00518024295397,0.011780101333188108,816.8426666666667,0,0,400,102727680,1362544,,,,,, +poll,branch,2,1024,True,1500,7046689667,212.86590880035132,0.2078768640628431,5632.858666666667,0,0,400,117735424,1363512,,,,,, +poll,branch,2,8192,True,1500,7374559625,203.40197601968674,1.5890779376538027,41472.85866666667,1,1,400,156368896,1369336,,,,,, +poll,branch,2,65536,True,800,3512677958,227.74646852496917,14.234154282810573,328192.99,1,1,0,398704640,1426184,,,,,, +offer,branch,2,1024,False,50000,3961911458,12620.170978086508,12.324385720787605,3178.26976,1,2,0,179945472,,,,,,, +poll,branch,2,1024,False,1500,7454393292,201.22361958146064,0.19650744099752016,5632.858666666667,0,0,400,117112832,1363080,,,,,, +latency,branch,2,1024,True,400,,,,8953.98,0,0,440,66568192,,8338.5,13484.583,22081.167,24165.334,9136.098970000001, +openclose,branch,2,1024,True,100,,,,,,,0,631881728,,9382.209,13362.292,17935.417,,9910.130019999999,5000 +offer,main,2,64,True,50000,4583327125,10909.10568597043,0.6658389700909686,297.51952,0,0,0,92110848,,,,,,, +offer,main,2,1024,True,50000,3949739959,12659.061234162631,12.362364486486944,3177.51952,1,1,0,168034304,,,,,,, +offer,main,2,8192,True,10000,810236833,12342.070358581193,96.42242467641557,24681.7176,1,1,0,278773760,,,,,,, +offer,main,2,65536,True,2000,285766333,6998.725073747578,437.4203171092236,196714.132,1,1,0,378093568,,,,,,, +poll,main,2,64,True,1500,7493254625,200.1800385903742,0.01221801993349452,712.768,0,0,400,102531072,1341024,,,,,, +poll,main,2,1024,True,1500,7223594750,207.6528448664704,0.2027859813149125,5512.768,0,0,400,116555776,1341992,,,,,, +poll,main,2,8192,True,1500,7198178083,208.3860641823468,1.6280161264245843,41352.768,1,1,400,164331520,1348584,,,,,, +poll,main,2,65536,True,800,3845947083,208.0111823525056,13.0006988970316,328072.99,1,2,0,398360576,1405568,,,,,, +offer,main,2,1024,False,50000,3760185083,13297.217795489032,12.985564253407258,3177.51952,1,1,0,167051264,,,,,,, +poll,main,2,1024,False,1500,7163141750,209.40532134520444,0.2044973841261762,5512.768,0,0,400,117096448,1341560,,,,,, +latency,main,2,1024,True,400,,,,8785.98,0,0,440,66338816,,8692.042,10375.917,11827.334,21839.833,8835.05769, +openclose,main,2,1024,True,100,,,,,,,0,624246784,,8634.791,11371.583,11968.708,,8991.40748,5000 +offer,main,3,64,True,50000,4239211416,11794.646478655359,0.7198880907382421,297.51952,0,0,0,91963392,,,,,,, +offer,main,3,1024,True,50000,4149411333,12049.902019198056,11.767482440623102,3177.51952,1,1,0,167542784,,,,,,, +offer,main,3,8192,True,10000,848592250,11784.222634604546,92.06423933284802,24681.7176,1,1,0,278773760,,,,,,, +offer,main,3,65536,True,2000,278540334,7180.288654353376,448.768040897086,196714.132,1,1,0,378273792,,,,,,, +poll,main,3,64,True,1500,7517277000,199.5403388753667,0.012178975761435956,712.768,0,0,400,102481920,1341024,,,,,, +poll,main,3,1024,True,1500,6811200084,220.22550820722594,0.21506397285861908,5512.768,0,0,400,116424704,1341992,,,,,, +poll,main,3,8192,True,1500,7904577250,189.7634690077828,1.482527101623303,41352.768,1,1,400,165691392,1348584,,,,,, +poll,main,3,65536,True,800,3699553916,216.24228708767384,13.515142942979615,328072.99,1,4,0,397918208,1405568,,,,,, +offer,main,3,1024,False,50000,3708398916,13482.907619316109,13.166901971988388,3177.51952,1,1,0,167280640,,,,,,, +poll,main,3,1024,False,1500,7390801958,202.9549713987885,0.1981982142566294,5512.768,0,0,400,116375552,1341560,,,,,, +latency,main,3,1024,True,400,,,,8785.98,0,0,440,66584576,,8497.208,10240.416,10600.541,12375.125,8619.909305, +openclose,main,3,1024,True,100,,,,,,,0,620904448,,8594.625,11391.667,13885.792,,7815.41962,5000 +offer,branch,3,64,True,50000,4588431625,10896.96961540753,0.665098243127901,298.26976,0,0,0,93192192,,,,,,, +offer,branch,3,1024,True,50000,3972017167,12588.062412067617,12.293029699284782,3178.26976,1,1,0,179421184,,,,,,, +offer,branch,3,8192,True,10000,831891916,12020.792374186263,93.91244042333018,24682.4224,1,1,0,289783808,,,,,,, +offer,branch,3,65536,True,2000,284486500,7030.21057238217,439.3881607738856,196714.888,1,1,0,378339328,,,,,,, +poll,branch,3,64,True,1500,7480229416,200.5286090278919,0.012239294984612542,816.8426666666667,0,0,400,102514688,1362544,,,,,, +poll,branch,3,1024,True,1500,6808467000,220.3139120744802,0.21515030476023458,5632.858666666667,0,0,400,117227520,1363512,,,,,, +poll,branch,3,8192,True,1500,7677230417,195.3829595472984,1.5264293714632688,41472.85866666667,1,10,400,156155904,1369336,,,,,, +poll,branch,3,65536,True,800,3617643083,221.13845441507308,13.821153400942068,328192.99,1,2,0,398557184,1426184,,,,,, +offer,branch,3,1024,False,50000,3751664792,13327.416699546115,13.015055370650503,3178.26976,1,2,0,180404224,,,,,,, +poll,branch,3,1024,False,1500,7555413417,198.53314666076864,0.19388002603590687,5632.858666666667,0,0,400,117178368,1363080,,,,,, +latency,branch,3,1024,True,400,,,,8953.98,0,0,440,66551808,,8426.375,9842.084,11719.167,16702.458,8573.51821, +openclose,branch,3,1024,True,100,,,,,,,0,648626176,,6410.375,10846.375,11850.75,,7420.27625,5000 +offer,branch,4,64,True,50000,4400786708,11361.604939659348,0.6934573327428801,298.26976,0,0,0,92946432,,,,,,, +offer,branch,4,1024,True,50000,3970737625,12592.118825781143,12.296991040801897,3178.26976,1,1,0,181190656,,,,,,, +offer,branch,4,8192,True,10000,837892958,11934.698704079572,93.23983362562166,24682.436,1,1,0,290619392,,,,,,, +offer,branch,4,65536,True,2000,296989542,6734.243861017841,420.89024131361504,196714.888,1,1,0,378372096,,,,,,, +poll,branch,4,64,True,1500,6450727459,232.53191357622973,0.014192621678236678,816.8426666666667,0,0,400,102891520,1362544,,,,,, +poll,branch,4,1024,True,1500,6498026042,230.83933340752222,0.22542903653078342,5632.858666666667,0,0,400,117473280,1363512,,,,,, +poll,branch,4,8192,True,1500,7453021917,201.26064523955966,1.5723487909340599,41472.85866666667,1,1,400,156073984,1369336,,,,,, +poll,branch,4,65536,True,800,3690777375,216.7565037704286,13.547281485651787,328192.99,1,3,0,398245888,1426184,,,,,, +offer,branch,4,1024,False,50000,3871007459,12916.534139904896,12.613802871000875,3178.26976,1,1,0,179421184,,,,,,, +poll,branch,4,1024,False,1500,7359922333,203.80649851077715,0.1990297837019308,5632.858666666667,0,0,400,117260288,1363080,,,,,, +latency,branch,4,1024,True,400,,,,8953.98,0,0,440,66650112,,8576.792,10670.958,14498.417,26410.958,8883.588035, +openclose,branch,4,1024,True,100,,,,,,,0,636944384,,9364.5,12107.458,13214.584,,9488.85045,5000 +offer,main,4,64,True,50000,3465804250,14426.665903015151,0.8805338075570771,297.51952,0,0,0,92487680,,,,,,, +offer,main,4,1024,True,50000,4897164708,10209.989449266446,9.970692821549264,3177.51952,1,0,0,168329216,,,,,,, +offer,main,4,8192,True,10000,891990459,11210.88224554653,87.58501754333227,24681.7176,1,1,0,277430272,,,,,,, +offer,main,4,65536,True,2000,302970375,6601.3054906771,412.5815931673188,196714.132,1,1,0,378191872,,,,,,, +poll,main,4,64,True,1500,6730290584,222.87299207644435,0.013603087895290793,712.768,0,0,400,102187008,1341024,,,,,, +poll,main,4,1024,True,1500,6899813500,217.39718037306372,0.21230193395807004,5512.768,0,0,400,116867072,1341992,,,,,, +poll,main,4,8192,True,1500,7415410083,202.28146295493283,1.5803239293354128,41352.768,1,0,400,165314560,1348584,,,,,, +poll,main,4,65536,True,800,3926511334,203.7432040683879,12.733950254274244,328072.99,1,4,0,397787136,1405568,,,,,, +offer,main,4,1024,False,50000,3687784291,13558.276746832642,13.240504635578752,3177.51952,1,1,0,167870464,,,,,,, +poll,main,4,1024,False,1500,7218635708,207.79549774725936,0.20292529076880797,5512.768,0,0,400,116555776,1341560,,,,,, +latency,main,4,1024,True,400,,,,8785.98,0,0,440,66584576,,8730.75,10501.541,11791.0,26743.792,8981.516679999999, +openclose,main,4,1024,True,100,,,,,,,0,621051904,,8323.042,10462.209,15246.458,,7624.9687300000005,5000 +offer,main,5,64,True,50000,3427266167,14588.887341588255,0.8904350184074863,297.51952,0,0,0,92192768,,,,,,, +offer,main,5,1024,True,50000,4671545583,10703.095819497647,10.45224201122817,3177.51952,1,1,0,167870464,,,,,,, +offer,main,5,8192,True,10000,871299125,11477.114705010177,89.66495863289201,24681.7176,1,1,0,278872064,,,,,,, +offer,main,5,65536,True,2000,276419708,7235.37411449693,452.2108821560581,196714.132,1,1,0,378421248,,,,,,, +poll,main,5,64,True,1500,6329604833,236.98161884919048,0.014464210134838286,712.768,0,0,400,102236160,1341024,,,,,, +poll,main,5,1024,True,1500,7385098750,203.11170517523547,0.1983512745851909,5512.768,0,0,400,116883456,1341992,,,,,, +poll,main,5,8192,True,1500,6545489333,229.1654487064153,1.7903550680188696,41352.768,1,3,400,163921920,1348584,,,,,, +poll,main,5,65536,True,800,4774399000,167.56035681140182,10.472522300712614,328072.99,1,1,0,396754944,1405568,,,,,, +offer,main,5,1024,False,50000,3966331583,12606.106916099456,12.310651285253375,3177.51952,1,1,0,168067072,,,,,,, +poll,main,5,1024,False,1500,7309338458,205.21693018035916,0.200407158379257,5512.768,0,0,400,116621312,1341560,,,,,, +latency,main,5,1024,True,400,,,,8785.98,0,0,440,66813952,,8754.875,11181.167,18663.25,31651.958,9304.376695, +openclose,main,5,1024,True,100,,,,,,,0,628375552,,9139.25,11704.083,20844.75,,9526.90207,5000 +offer,branch,5,64,True,50000,3533776959,14149.166905584547,0.8635966128896818,298.26976,0,0,0,94240768,,,,,,, +offer,branch,5,1024,True,50000,4768045375,10486.477385924625,10.240700572192017,3178.26976,1,1,0,179617792,,,,,,, +offer,branch,5,8192,True,10000,864393375,11568.806852551363,90.38130353555752,24682.436,1,1,0,290160640,,,,,,, +offer,branch,5,65536,True,2000,294764417,6785.079489428333,424.0674680892708,196714.888,1,1,0,378585088,,,,,,, +poll,branch,5,64,True,1500,6402192750,234.29472659972632,0.014300215246565327,816.8426666666667,0,0,400,102809600,1362544,,,,,, +poll,branch,5,1024,True,1500,7493172708,200.18222700226062,0.19549045605689513,5632.858666666667,0,0,400,116867072,1363512,,,,,, +poll,branch,5,8192,True,1500,6768218458,221.624052076364,1.7314379068465937,41472.85866666667,1,1,400,154157056,1369336,,,,,, +poll,branch,5,65536,True,800,4803512834,166.54478246367478,10.409048903979674,328192.99,1,1,0,399687680,1426184,,,,,, +offer,branch,5,1024,False,50000,4013190000,12458.916722108846,12.16691086143442,3178.26976,1,1,0,179552256,,,,,,, +poll,branch,5,1024,False,1500,7855651583,190.94533205190396,0.18647005083193746,5632.858666666667,0,0,400,116736000,1363080,,,,,, +latency,branch,5,1024,True,400,,,,8954.7,0,0,440,66666496,,9415.083,12470.083,17532.0,27803.125,9733.8479225, +openclose,branch,5,1024,True,100,,,,,,,0,616972288,,9355.708,13790.833,17520.5,,9713.01207,5000 diff --git a/benchmarks/results/raw.jsonl b/benchmarks/results/raw.jsonl new file mode 100644 index 0000000..0bb7e23 --- /dev/null +++ b/benchmarks/results/raw.jsonl @@ -0,0 +1,120 @@ +{"scenario":"offer","side":"main","trial":1,"size":64,"checksum":true,"ops":50000,"elapsed_ns":3719083042,"msgs_per_sec":13444.174124466888,"mib_per_sec":0.8205672683390435,"alloc_bytes_per_op":297.51952,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92078080} +{"scenario":"offer","side":"main","trial":1,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":4017336666,"msgs_per_sec":12446.056717916108,"mib_per_sec":12.15435226358995,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":10,"sink":0,"peak_rss_bytes":167804928} +{"scenario":"offer","side":"main","trial":1,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":837004041,"msgs_per_sec":11947.373620864037,"mib_per_sec":93.33885641300029,"alloc_bytes_per_op":24681.7176,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":279183360} +{"scenario":"offer","side":"main","trial":1,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":280542416,"msgs_per_sec":7129.046753486289,"mib_per_sec":445.5654220928931,"alloc_bytes_per_op":196714.132,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378224640} +{"scenario":"poll","side":"main","trial":1,"size":64,"checksum":true,"ops":1500,"elapsed_ns":6996561084,"msgs_per_sec":214.39103896773753,"mib_per_sec":0.013085390561995699,"alloc_bytes_per_op":712.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341024,"sink":400,"peak_rss_bytes":101957632} +{"scenario":"poll","side":"main","trial":1,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":6883683125,"msgs_per_sec":217.90660214331118,"mib_per_sec":0.21279941615557732,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341992,"sink":400,"peak_rss_bytes":116834304} +{"scenario":"poll","side":"main","trial":1,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7575483917,"msgs_per_sec":198.00715260366118,"mib_per_sec":1.546930879716103,"alloc_bytes_per_op":41352.768,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1348584,"sink":400,"peak_rss_bytes":164954112} +{"scenario":"poll","side":"main","trial":1,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3385521625,"msgs_per_sec":236.3003662692599,"mib_per_sec":14.768772891828744,"alloc_bytes_per_op":328072.99,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1405568,"sink":0,"peak_rss_bytes":398147584} +{"scenario":"offer","side":"main","trial":1,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3645314875,"msgs_per_sec":13716.236241457742,"mib_per_sec":13.394761954548576,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":168361984} +{"scenario":"poll","side":"main","trial":1,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":6559579792,"msgs_per_sec":228.67318449718158,"mib_per_sec":0.2233136567355289,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341560,"sink":400,"peak_rss_bytes":117112832} +{"scenario":"latency","side":"main","trial":1,"size":1024,"checksum":true,"ops":400,"p50_us":8403.167,"p95_us":10149.792,"p99_us":18061.75,"max_us":22383.291,"mean_us":8710.1485675,"alloc_bytes_per_op":8785.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66420736} +{"scenario":"openclose","side":"main","trial":1,"size":1024,"checksum":true,"ops":100,"p50_us":9678.083,"p95_us":12694.958,"p99_us":17816.833,"mean_us":9867.78131,"populated_msgs":5000,"sink":0,"peak_rss_bytes":620085248} +{"scenario":"offer","side":"branch","trial":1,"size":64,"checksum":true,"ops":50000,"elapsed_ns":4827782500,"msgs_per_sec":10356.721745439028,"mib_per_sec":0.6321241299706438,"alloc_bytes_per_op":298.27248,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92749824} +{"scenario":"offer","side":"branch","trial":1,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":3739233500,"msgs_per_sec":13371.724445665135,"mib_per_sec":13.058324653969859,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":2,"sink":0,"peak_rss_bytes":178978816} +{"scenario":"offer","side":"branch","trial":1,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":817242417,"msgs_per_sec":12236.271382864357,"mib_per_sec":95.59587017862779,"alloc_bytes_per_op":24682.436,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":291602432} +{"scenario":"offer","side":"branch","trial":1,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":295911458,"msgs_per_sec":6758.778499209044,"mib_per_sec":422.42365620056523,"alloc_bytes_per_op":196714.888,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378306560} +{"scenario":"poll","side":"branch","trial":1,"size":64,"checksum":true,"ops":1500,"elapsed_ns":7548540917,"msgs_per_sec":198.71389934733793,"mib_per_sec":0.012128533895711543,"alloc_bytes_per_op":816.8426666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1362544,"sink":400,"peak_rss_bytes":102907904} +{"scenario":"poll","side":"branch","trial":1,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":6493434333,"msgs_per_sec":231.0025670663851,"mib_per_sec":0.2255884444007667,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363512,"sink":400,"peak_rss_bytes":117358592} +{"scenario":"poll","side":"branch","trial":1,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7661396958,"msgs_per_sec":195.7867485816286,"mib_per_sec":1.5295839732939733,"alloc_bytes_per_op":41472.85866666667,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1369336,"sink":400,"peak_rss_bytes":156352512} +{"scenario":"poll","side":"branch","trial":1,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3742801500,"msgs_per_sec":213.7436356162623,"mib_per_sec":13.358977226016394,"alloc_bytes_per_op":328192.99,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1426184,"sink":0,"peak_rss_bytes":398622720} +{"scenario":"offer","side":"branch","trial":1,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3738086667,"msgs_per_sec":13375.826847836966,"mib_per_sec":13.062330906090788,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":181452800} +{"scenario":"poll","side":"branch","trial":1,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":6711036709,"msgs_per_sec":223.51241172446402,"mib_per_sec":0.2182738395746719,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363080,"sink":400,"peak_rss_bytes":117342208} +{"scenario":"latency","side":"branch","trial":1,"size":1024,"checksum":true,"ops":400,"p50_us":8657.916,"p95_us":12844.792,"p99_us":17035.958,"max_us":27945.333,"mean_us":9268.424565,"alloc_bytes_per_op":8954.7,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66797568} +{"scenario":"openclose","side":"branch","trial":1,"size":1024,"checksum":true,"ops":100,"p50_us":9446.375,"p95_us":12437.958,"p99_us":15472.083,"mean_us":9700.30919,"populated_msgs":5000,"sink":0,"peak_rss_bytes":619413504} +{"scenario":"offer","side":"branch","trial":2,"size":64,"checksum":true,"ops":50000,"elapsed_ns":3733240625,"msgs_per_sec":13393.189730436945,"mib_per_sec":0.8174554278831143,"alloc_bytes_per_op":298.27248,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92946432} +{"scenario":"offer","side":"branch","trial":2,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":3912723708,"msgs_per_sec":12778.822051188901,"mib_per_sec":12.479318409364161,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":178683904} +{"scenario":"offer","side":"branch","trial":2,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":808486458,"msgs_per_sec":12368.790968667035,"mib_per_sec":96.63117944271121,"alloc_bytes_per_op":24682.436,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":289538048} +{"scenario":"offer","side":"branch","trial":2,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":268522208,"msgs_per_sec":7448.173523137423,"mib_per_sec":465.51084519608895,"alloc_bytes_per_op":196714.888,"gc_count":1,"gc_time_ms":0,"sink":0,"peak_rss_bytes":378617856} +{"scenario":"poll","side":"branch","trial":2,"size":64,"checksum":true,"ops":1500,"elapsed_ns":7771812125,"msgs_per_sec":193.00518024295397,"mib_per_sec":0.011780101333188108,"alloc_bytes_per_op":816.8426666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1362544,"sink":400,"peak_rss_bytes":102727680} +{"scenario":"poll","side":"branch","trial":2,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":7046689667,"msgs_per_sec":212.86590880035132,"mib_per_sec":0.2078768640628431,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363512,"sink":400,"peak_rss_bytes":117735424} +{"scenario":"poll","side":"branch","trial":2,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7374559625,"msgs_per_sec":203.40197601968674,"mib_per_sec":1.5890779376538027,"alloc_bytes_per_op":41472.85866666667,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1369336,"sink":400,"peak_rss_bytes":156368896} +{"scenario":"poll","side":"branch","trial":2,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3512677958,"msgs_per_sec":227.74646852496917,"mib_per_sec":14.234154282810573,"alloc_bytes_per_op":328192.99,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1426184,"sink":0,"peak_rss_bytes":398704640} +{"scenario":"offer","side":"branch","trial":2,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3961911458,"msgs_per_sec":12620.170978086508,"mib_per_sec":12.324385720787605,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":2,"sink":0,"peak_rss_bytes":179945472} +{"scenario":"poll","side":"branch","trial":2,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7454393292,"msgs_per_sec":201.22361958146064,"mib_per_sec":0.19650744099752016,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363080,"sink":400,"peak_rss_bytes":117112832} +{"scenario":"latency","side":"branch","trial":2,"size":1024,"checksum":true,"ops":400,"p50_us":8338.5,"p95_us":13484.583,"p99_us":22081.167,"max_us":24165.334,"mean_us":9136.098970000001,"alloc_bytes_per_op":8953.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66568192} +{"scenario":"openclose","side":"branch","trial":2,"size":1024,"checksum":true,"ops":100,"p50_us":9382.209,"p95_us":13362.292,"p99_us":17935.417,"mean_us":9910.130019999999,"populated_msgs":5000,"sink":0,"peak_rss_bytes":631881728} +{"scenario":"offer","side":"main","trial":2,"size":64,"checksum":true,"ops":50000,"elapsed_ns":4583327125,"msgs_per_sec":10909.10568597043,"mib_per_sec":0.6658389700909686,"alloc_bytes_per_op":297.51952,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92110848} +{"scenario":"offer","side":"main","trial":2,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":3949739959,"msgs_per_sec":12659.061234162631,"mib_per_sec":12.362364486486944,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":168034304} +{"scenario":"offer","side":"main","trial":2,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":810236833,"msgs_per_sec":12342.070358581193,"mib_per_sec":96.42242467641557,"alloc_bytes_per_op":24681.7176,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":278773760} +{"scenario":"offer","side":"main","trial":2,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":285766333,"msgs_per_sec":6998.725073747578,"mib_per_sec":437.4203171092236,"alloc_bytes_per_op":196714.132,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378093568} +{"scenario":"poll","side":"main","trial":2,"size":64,"checksum":true,"ops":1500,"elapsed_ns":7493254625,"msgs_per_sec":200.1800385903742,"mib_per_sec":0.01221801993349452,"alloc_bytes_per_op":712.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341024,"sink":400,"peak_rss_bytes":102531072} +{"scenario":"poll","side":"main","trial":2,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":7223594750,"msgs_per_sec":207.6528448664704,"mib_per_sec":0.2027859813149125,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341992,"sink":400,"peak_rss_bytes":116555776} +{"scenario":"poll","side":"main","trial":2,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7198178083,"msgs_per_sec":208.3860641823468,"mib_per_sec":1.6280161264245843,"alloc_bytes_per_op":41352.768,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1348584,"sink":400,"peak_rss_bytes":164331520} +{"scenario":"poll","side":"main","trial":2,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3845947083,"msgs_per_sec":208.0111823525056,"mib_per_sec":13.0006988970316,"alloc_bytes_per_op":328072.99,"gc_count":1,"gc_time_ms":2,"heap_after_cycle_bytes":1405568,"sink":0,"peak_rss_bytes":398360576} +{"scenario":"offer","side":"main","trial":2,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3760185083,"msgs_per_sec":13297.217795489032,"mib_per_sec":12.985564253407258,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":167051264} +{"scenario":"poll","side":"main","trial":2,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7163141750,"msgs_per_sec":209.40532134520444,"mib_per_sec":0.2044973841261762,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341560,"sink":400,"peak_rss_bytes":117096448} +{"scenario":"latency","side":"main","trial":2,"size":1024,"checksum":true,"ops":400,"p50_us":8692.042,"p95_us":10375.917,"p99_us":11827.334,"max_us":21839.833,"mean_us":8835.05769,"alloc_bytes_per_op":8785.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66338816} +{"scenario":"openclose","side":"main","trial":2,"size":1024,"checksum":true,"ops":100,"p50_us":8634.791,"p95_us":11371.583,"p99_us":11968.708,"mean_us":8991.40748,"populated_msgs":5000,"sink":0,"peak_rss_bytes":624246784} +{"scenario":"offer","side":"main","trial":3,"size":64,"checksum":true,"ops":50000,"elapsed_ns":4239211416,"msgs_per_sec":11794.646478655359,"mib_per_sec":0.7198880907382421,"alloc_bytes_per_op":297.51952,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":91963392} +{"scenario":"offer","side":"main","trial":3,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":4149411333,"msgs_per_sec":12049.902019198056,"mib_per_sec":11.767482440623102,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":167542784} +{"scenario":"offer","side":"main","trial":3,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":848592250,"msgs_per_sec":11784.222634604546,"mib_per_sec":92.06423933284802,"alloc_bytes_per_op":24681.7176,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":278773760} +{"scenario":"offer","side":"main","trial":3,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":278540334,"msgs_per_sec":7180.288654353376,"mib_per_sec":448.768040897086,"alloc_bytes_per_op":196714.132,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378273792} +{"scenario":"poll","side":"main","trial":3,"size":64,"checksum":true,"ops":1500,"elapsed_ns":7517277000,"msgs_per_sec":199.5403388753667,"mib_per_sec":0.012178975761435956,"alloc_bytes_per_op":712.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341024,"sink":400,"peak_rss_bytes":102481920} +{"scenario":"poll","side":"main","trial":3,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":6811200084,"msgs_per_sec":220.22550820722594,"mib_per_sec":0.21506397285861908,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341992,"sink":400,"peak_rss_bytes":116424704} +{"scenario":"poll","side":"main","trial":3,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7904577250,"msgs_per_sec":189.7634690077828,"mib_per_sec":1.482527101623303,"alloc_bytes_per_op":41352.768,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1348584,"sink":400,"peak_rss_bytes":165691392} +{"scenario":"poll","side":"main","trial":3,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3699553916,"msgs_per_sec":216.24228708767384,"mib_per_sec":13.515142942979615,"alloc_bytes_per_op":328072.99,"gc_count":1,"gc_time_ms":4,"heap_after_cycle_bytes":1405568,"sink":0,"peak_rss_bytes":397918208} +{"scenario":"offer","side":"main","trial":3,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3708398916,"msgs_per_sec":13482.907619316109,"mib_per_sec":13.166901971988388,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":167280640} +{"scenario":"poll","side":"main","trial":3,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7390801958,"msgs_per_sec":202.9549713987885,"mib_per_sec":0.1981982142566294,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341560,"sink":400,"peak_rss_bytes":116375552} +{"scenario":"latency","side":"main","trial":3,"size":1024,"checksum":true,"ops":400,"p50_us":8497.208,"p95_us":10240.416,"p99_us":10600.541,"max_us":12375.125,"mean_us":8619.909305,"alloc_bytes_per_op":8785.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66584576} +{"scenario":"openclose","side":"main","trial":3,"size":1024,"checksum":true,"ops":100,"p50_us":8594.625,"p95_us":11391.667,"p99_us":13885.792,"mean_us":7815.41962,"populated_msgs":5000,"sink":0,"peak_rss_bytes":620904448} +{"scenario":"offer","side":"branch","trial":3,"size":64,"checksum":true,"ops":50000,"elapsed_ns":4588431625,"msgs_per_sec":10896.96961540753,"mib_per_sec":0.665098243127901,"alloc_bytes_per_op":298.26976,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":93192192} +{"scenario":"offer","side":"branch","trial":3,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":3972017167,"msgs_per_sec":12588.062412067617,"mib_per_sec":12.293029699284782,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":179421184} +{"scenario":"offer","side":"branch","trial":3,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":831891916,"msgs_per_sec":12020.792374186263,"mib_per_sec":93.91244042333018,"alloc_bytes_per_op":24682.4224,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":289783808} +{"scenario":"offer","side":"branch","trial":3,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":284486500,"msgs_per_sec":7030.21057238217,"mib_per_sec":439.3881607738856,"alloc_bytes_per_op":196714.888,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378339328} +{"scenario":"poll","side":"branch","trial":3,"size":64,"checksum":true,"ops":1500,"elapsed_ns":7480229416,"msgs_per_sec":200.5286090278919,"mib_per_sec":0.012239294984612542,"alloc_bytes_per_op":816.8426666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1362544,"sink":400,"peak_rss_bytes":102514688} +{"scenario":"poll","side":"branch","trial":3,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":6808467000,"msgs_per_sec":220.3139120744802,"mib_per_sec":0.21515030476023458,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363512,"sink":400,"peak_rss_bytes":117227520} +{"scenario":"poll","side":"branch","trial":3,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7677230417,"msgs_per_sec":195.3829595472984,"mib_per_sec":1.5264293714632688,"alloc_bytes_per_op":41472.85866666667,"gc_count":1,"gc_time_ms":10,"heap_after_cycle_bytes":1369336,"sink":400,"peak_rss_bytes":156155904} +{"scenario":"poll","side":"branch","trial":3,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3617643083,"msgs_per_sec":221.13845441507308,"mib_per_sec":13.821153400942068,"alloc_bytes_per_op":328192.99,"gc_count":1,"gc_time_ms":2,"heap_after_cycle_bytes":1426184,"sink":0,"peak_rss_bytes":398557184} +{"scenario":"offer","side":"branch","trial":3,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3751664792,"msgs_per_sec":13327.416699546115,"mib_per_sec":13.015055370650503,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":2,"sink":0,"peak_rss_bytes":180404224} +{"scenario":"poll","side":"branch","trial":3,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7555413417,"msgs_per_sec":198.53314666076864,"mib_per_sec":0.19388002603590687,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363080,"sink":400,"peak_rss_bytes":117178368} +{"scenario":"latency","side":"branch","trial":3,"size":1024,"checksum":true,"ops":400,"p50_us":8426.375,"p95_us":9842.084,"p99_us":11719.167,"max_us":16702.458,"mean_us":8573.51821,"alloc_bytes_per_op":8953.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66551808} +{"scenario":"openclose","side":"branch","trial":3,"size":1024,"checksum":true,"ops":100,"p50_us":6410.375,"p95_us":10846.375,"p99_us":11850.75,"mean_us":7420.27625,"populated_msgs":5000,"sink":0,"peak_rss_bytes":648626176} +{"scenario":"offer","side":"branch","trial":4,"size":64,"checksum":true,"ops":50000,"elapsed_ns":4400786708,"msgs_per_sec":11361.604939659348,"mib_per_sec":0.6934573327428801,"alloc_bytes_per_op":298.26976,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92946432} +{"scenario":"offer","side":"branch","trial":4,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":3970737625,"msgs_per_sec":12592.118825781143,"mib_per_sec":12.296991040801897,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":181190656} +{"scenario":"offer","side":"branch","trial":4,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":837892958,"msgs_per_sec":11934.698704079572,"mib_per_sec":93.23983362562166,"alloc_bytes_per_op":24682.436,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":290619392} +{"scenario":"offer","side":"branch","trial":4,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":296989542,"msgs_per_sec":6734.243861017841,"mib_per_sec":420.89024131361504,"alloc_bytes_per_op":196714.888,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378372096} +{"scenario":"poll","side":"branch","trial":4,"size":64,"checksum":true,"ops":1500,"elapsed_ns":6450727459,"msgs_per_sec":232.53191357622973,"mib_per_sec":0.014192621678236678,"alloc_bytes_per_op":816.8426666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1362544,"sink":400,"peak_rss_bytes":102891520} +{"scenario":"poll","side":"branch","trial":4,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":6498026042,"msgs_per_sec":230.83933340752222,"mib_per_sec":0.22542903653078342,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363512,"sink":400,"peak_rss_bytes":117473280} +{"scenario":"poll","side":"branch","trial":4,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7453021917,"msgs_per_sec":201.26064523955966,"mib_per_sec":1.5723487909340599,"alloc_bytes_per_op":41472.85866666667,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1369336,"sink":400,"peak_rss_bytes":156073984} +{"scenario":"poll","side":"branch","trial":4,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3690777375,"msgs_per_sec":216.7565037704286,"mib_per_sec":13.547281485651787,"alloc_bytes_per_op":328192.99,"gc_count":1,"gc_time_ms":3,"heap_after_cycle_bytes":1426184,"sink":0,"peak_rss_bytes":398245888} +{"scenario":"offer","side":"branch","trial":4,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3871007459,"msgs_per_sec":12916.534139904896,"mib_per_sec":12.613802871000875,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":179421184} +{"scenario":"poll","side":"branch","trial":4,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7359922333,"msgs_per_sec":203.80649851077715,"mib_per_sec":0.1990297837019308,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363080,"sink":400,"peak_rss_bytes":117260288} +{"scenario":"latency","side":"branch","trial":4,"size":1024,"checksum":true,"ops":400,"p50_us":8576.792,"p95_us":10670.958,"p99_us":14498.417,"max_us":26410.958,"mean_us":8883.588035,"alloc_bytes_per_op":8953.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66650112} +{"scenario":"openclose","side":"branch","trial":4,"size":1024,"checksum":true,"ops":100,"p50_us":9364.5,"p95_us":12107.458,"p99_us":13214.584,"mean_us":9488.85045,"populated_msgs":5000,"sink":0,"peak_rss_bytes":636944384} +{"scenario":"offer","side":"main","trial":4,"size":64,"checksum":true,"ops":50000,"elapsed_ns":3465804250,"msgs_per_sec":14426.665903015151,"mib_per_sec":0.8805338075570771,"alloc_bytes_per_op":297.51952,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92487680} +{"scenario":"offer","side":"main","trial":4,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":4897164708,"msgs_per_sec":10209.989449266446,"mib_per_sec":9.970692821549264,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":0,"sink":0,"peak_rss_bytes":168329216} +{"scenario":"offer","side":"main","trial":4,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":891990459,"msgs_per_sec":11210.88224554653,"mib_per_sec":87.58501754333227,"alloc_bytes_per_op":24681.7176,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":277430272} +{"scenario":"offer","side":"main","trial":4,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":302970375,"msgs_per_sec":6601.3054906771,"mib_per_sec":412.5815931673188,"alloc_bytes_per_op":196714.132,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378191872} +{"scenario":"poll","side":"main","trial":4,"size":64,"checksum":true,"ops":1500,"elapsed_ns":6730290584,"msgs_per_sec":222.87299207644435,"mib_per_sec":0.013603087895290793,"alloc_bytes_per_op":712.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341024,"sink":400,"peak_rss_bytes":102187008} +{"scenario":"poll","side":"main","trial":4,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":6899813500,"msgs_per_sec":217.39718037306372,"mib_per_sec":0.21230193395807004,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341992,"sink":400,"peak_rss_bytes":116867072} +{"scenario":"poll","side":"main","trial":4,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":7415410083,"msgs_per_sec":202.28146295493283,"mib_per_sec":1.5803239293354128,"alloc_bytes_per_op":41352.768,"gc_count":1,"gc_time_ms":0,"heap_after_cycle_bytes":1348584,"sink":400,"peak_rss_bytes":165314560} +{"scenario":"poll","side":"main","trial":4,"size":65536,"checksum":true,"ops":800,"elapsed_ns":3926511334,"msgs_per_sec":203.7432040683879,"mib_per_sec":12.733950254274244,"alloc_bytes_per_op":328072.99,"gc_count":1,"gc_time_ms":4,"heap_after_cycle_bytes":1405568,"sink":0,"peak_rss_bytes":397787136} +{"scenario":"offer","side":"main","trial":4,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3687784291,"msgs_per_sec":13558.276746832642,"mib_per_sec":13.240504635578752,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":167870464} +{"scenario":"poll","side":"main","trial":4,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7218635708,"msgs_per_sec":207.79549774725936,"mib_per_sec":0.20292529076880797,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341560,"sink":400,"peak_rss_bytes":116555776} +{"scenario":"latency","side":"main","trial":4,"size":1024,"checksum":true,"ops":400,"p50_us":8730.75,"p95_us":10501.541,"p99_us":11791.0,"max_us":26743.792,"mean_us":8981.516679999999,"alloc_bytes_per_op":8785.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66584576} +{"scenario":"openclose","side":"main","trial":4,"size":1024,"checksum":true,"ops":100,"p50_us":8323.042,"p95_us":10462.209,"p99_us":15246.458,"mean_us":7624.9687300000005,"populated_msgs":5000,"sink":0,"peak_rss_bytes":621051904} +{"scenario":"offer","side":"main","trial":5,"size":64,"checksum":true,"ops":50000,"elapsed_ns":3427266167,"msgs_per_sec":14588.887341588255,"mib_per_sec":0.8904350184074863,"alloc_bytes_per_op":297.51952,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":92192768} +{"scenario":"offer","side":"main","trial":5,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":4671545583,"msgs_per_sec":10703.095819497647,"mib_per_sec":10.45224201122817,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":167870464} +{"scenario":"offer","side":"main","trial":5,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":871299125,"msgs_per_sec":11477.114705010177,"mib_per_sec":89.66495863289201,"alloc_bytes_per_op":24681.7176,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":278872064} +{"scenario":"offer","side":"main","trial":5,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":276419708,"msgs_per_sec":7235.37411449693,"mib_per_sec":452.2108821560581,"alloc_bytes_per_op":196714.132,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378421248} +{"scenario":"poll","side":"main","trial":5,"size":64,"checksum":true,"ops":1500,"elapsed_ns":6329604833,"msgs_per_sec":236.98161884919048,"mib_per_sec":0.014464210134838286,"alloc_bytes_per_op":712.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341024,"sink":400,"peak_rss_bytes":102236160} +{"scenario":"poll","side":"main","trial":5,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":7385098750,"msgs_per_sec":203.11170517523547,"mib_per_sec":0.1983512745851909,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341992,"sink":400,"peak_rss_bytes":116883456} +{"scenario":"poll","side":"main","trial":5,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":6545489333,"msgs_per_sec":229.1654487064153,"mib_per_sec":1.7903550680188696,"alloc_bytes_per_op":41352.768,"gc_count":1,"gc_time_ms":3,"heap_after_cycle_bytes":1348584,"sink":400,"peak_rss_bytes":163921920} +{"scenario":"poll","side":"main","trial":5,"size":65536,"checksum":true,"ops":800,"elapsed_ns":4774399000,"msgs_per_sec":167.56035681140182,"mib_per_sec":10.472522300712614,"alloc_bytes_per_op":328072.99,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1405568,"sink":0,"peak_rss_bytes":396754944} +{"scenario":"offer","side":"main","trial":5,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":3966331583,"msgs_per_sec":12606.106916099456,"mib_per_sec":12.310651285253375,"alloc_bytes_per_op":3177.51952,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":168067072} +{"scenario":"poll","side":"main","trial":5,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7309338458,"msgs_per_sec":205.21693018035916,"mib_per_sec":0.200407158379257,"alloc_bytes_per_op":5512.768,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1341560,"sink":400,"peak_rss_bytes":116621312} +{"scenario":"latency","side":"main","trial":5,"size":1024,"checksum":true,"ops":400,"p50_us":8754.875,"p95_us":11181.167,"p99_us":18663.25,"max_us":31651.958,"mean_us":9304.376695,"alloc_bytes_per_op":8785.98,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66813952} +{"scenario":"openclose","side":"main","trial":5,"size":1024,"checksum":true,"ops":100,"p50_us":9139.25,"p95_us":11704.083,"p99_us":20844.75,"mean_us":9526.90207,"populated_msgs":5000,"sink":0,"peak_rss_bytes":628375552} +{"scenario":"offer","side":"branch","trial":5,"size":64,"checksum":true,"ops":50000,"elapsed_ns":3533776959,"msgs_per_sec":14149.166905584547,"mib_per_sec":0.8635966128896818,"alloc_bytes_per_op":298.26976,"gc_count":0,"gc_time_ms":0,"sink":0,"peak_rss_bytes":94240768} +{"scenario":"offer","side":"branch","trial":5,"size":1024,"checksum":true,"ops":50000,"elapsed_ns":4768045375,"msgs_per_sec":10486.477385924625,"mib_per_sec":10.240700572192017,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":179617792} +{"scenario":"offer","side":"branch","trial":5,"size":8192,"checksum":true,"ops":10000,"elapsed_ns":864393375,"msgs_per_sec":11568.806852551363,"mib_per_sec":90.38130353555752,"alloc_bytes_per_op":24682.436,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":290160640} +{"scenario":"offer","side":"branch","trial":5,"size":65536,"checksum":true,"ops":2000,"elapsed_ns":294764417,"msgs_per_sec":6785.079489428333,"mib_per_sec":424.0674680892708,"alloc_bytes_per_op":196714.888,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":378585088} +{"scenario":"poll","side":"branch","trial":5,"size":64,"checksum":true,"ops":1500,"elapsed_ns":6402192750,"msgs_per_sec":234.29472659972632,"mib_per_sec":0.014300215246565327,"alloc_bytes_per_op":816.8426666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1362544,"sink":400,"peak_rss_bytes":102809600} +{"scenario":"poll","side":"branch","trial":5,"size":1024,"checksum":true,"ops":1500,"elapsed_ns":7493172708,"msgs_per_sec":200.18222700226062,"mib_per_sec":0.19549045605689513,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363512,"sink":400,"peak_rss_bytes":116867072} +{"scenario":"poll","side":"branch","trial":5,"size":8192,"checksum":true,"ops":1500,"elapsed_ns":6768218458,"msgs_per_sec":221.624052076364,"mib_per_sec":1.7314379068465937,"alloc_bytes_per_op":41472.85866666667,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1369336,"sink":400,"peak_rss_bytes":154157056} +{"scenario":"poll","side":"branch","trial":5,"size":65536,"checksum":true,"ops":800,"elapsed_ns":4803512834,"msgs_per_sec":166.54478246367478,"mib_per_sec":10.409048903979674,"alloc_bytes_per_op":328192.99,"gc_count":1,"gc_time_ms":1,"heap_after_cycle_bytes":1426184,"sink":0,"peak_rss_bytes":399687680} +{"scenario":"offer","side":"branch","trial":5,"size":1024,"checksum":false,"ops":50000,"elapsed_ns":4013190000,"msgs_per_sec":12458.916722108846,"mib_per_sec":12.16691086143442,"alloc_bytes_per_op":3178.26976,"gc_count":1,"gc_time_ms":1,"sink":0,"peak_rss_bytes":179552256} +{"scenario":"poll","side":"branch","trial":5,"size":1024,"checksum":false,"ops":1500,"elapsed_ns":7855651583,"msgs_per_sec":190.94533205190396,"mib_per_sec":0.18647005083193746,"alloc_bytes_per_op":5632.858666666667,"gc_count":0,"gc_time_ms":0,"heap_after_cycle_bytes":1363080,"sink":400,"peak_rss_bytes":116736000} +{"scenario":"latency","side":"branch","trial":5,"size":1024,"checksum":true,"ops":400,"p50_us":9415.083,"p95_us":12470.083,"p99_us":17532.0,"max_us":27803.125,"mean_us":9733.8479225,"alloc_bytes_per_op":8954.7,"gc_count":0,"gc_time_ms":0,"sink":440,"peak_rss_bytes":66666496} +{"scenario":"openclose","side":"branch","trial":5,"size":1024,"checksum":true,"ops":100,"p50_us":9355.708,"p95_us":13790.833,"p99_us":17520.5,"mean_us":9713.01207,"populated_msgs":5000,"sink":0,"peak_rss_bytes":616972288} diff --git a/benchmarks/results/summary.md b/benchmarks/results/summary.md new file mode 100644 index 0000000..d9e3052 --- /dev/null +++ b/benchmarks/results/summary.md @@ -0,0 +1,83 @@ +# Benchmark summary: main vs this branch + +- JVM: openjdk version "21.0.12" 2026-07-21 LTS, flags `-Xms512m -Xmx512m` +- OS: Darwin arm64, date 2026-08-15T06:30:57Z +- Trials: 5 per side, interleaved (fresh JVM per trial). Values are medians. +- Before = `main` (c04e37b), after = this branch (de5f041). +- Delta = (after - before) / before. Positive throughput delta is better; + positive latency/memory delta is worse. Deltas within the run-to-run noise + band (max spread across trials of either side) are marked `~` (equivalent). + +## offer() throughput + +| Case | Metric | main | this branch | delta | +|---|---|---:|---:|---:| +| 64 B, checksum on | msgs_per_sec (msgs/s) | 13,444 | 11,362 | -15.5% ~ | +| 64 B, checksum on | mib_per_sec (MiB/s) | 0.8 | 0.7 | -15.5% ~ | +| 64 B, checksum on | alloc_bytes_per_op (B/op) | 298 | 298 | +0.3% | +| 64 B, checksum on | peak_rss_bytes (bytes) | 87.8 MiB | 88.6 MiB | +0.9% ~ | +| 1 KiB, checksum on | msgs_per_sec (msgs/s) | 12,050 | 12,592 | +4.5% ~ | +| 1 KiB, checksum on | mib_per_sec (MiB/s) | 11.8 | 12.3 | +4.5% ~ | +| 1 KiB, checksum on | alloc_bytes_per_op (B/op) | 3,178 | 3,178 | +0.0% | +| 1 KiB, checksum on | peak_rss_bytes (bytes) | 160.1 MiB | 171.1 MiB | +6.9% | +| 1 KiB, checksum off | msgs_per_sec (msgs/s) | 13,483 | 12,917 | -4.2% ~ | +| 1 KiB, checksum off | mib_per_sec (MiB/s) | 13.2 | 12.6 | -4.2% ~ | +| 1 KiB, checksum off | alloc_bytes_per_op (B/op) | 3,178 | 3,178 | +0.0% | +| 1 KiB, checksum off | peak_rss_bytes (bytes) | 160.1 MiB | 171.6 MiB | +7.2% | +| 8 KiB, checksum on | msgs_per_sec (msgs/s) | 11,784 | 12,021 | +2.0% ~ | +| 8 KiB, checksum on | mib_per_sec (MiB/s) | 92.1 | 93.9 | +2.0% ~ | +| 8 KiB, checksum on | alloc_bytes_per_op (B/op) | 24,682 | 24,682 | +0.0% | +| 8 KiB, checksum on | peak_rss_bytes (bytes) | 265.9 MiB | 276.7 MiB | +4.1% | +| 64 KiB, checksum on | msgs_per_sec (msgs/s) | 7,129 | 6,785 | -4.8% ~ | +| 64 KiB, checksum on | mib_per_sec (MiB/s) | 445.6 | 424.1 | -4.8% ~ | +| 64 KiB, checksum on | alloc_bytes_per_op (B/op) | 196,714 | 196,715 | +0.0% | +| 64 KiB, checksum on | peak_rss_bytes (bytes) | 360.7 MiB | 360.8 MiB | +0.0% ~ | + +## poll() throughput + +| Case | Metric | main | this branch | delta | +|---|---|---:|---:|---:| +| 64 B, checksum on | msgs_per_sec (msgs/s) | 214 | 201 | -6.5% ~ | +| 64 B, checksum on | mib_per_sec (MiB/s) | 0.0 | 0.0 | -6.5% ~ | +| 64 B, checksum on | alloc_bytes_per_op (B/op) | 713 | 817 | +14.6% | +| 64 B, checksum on | heap_after_cycle_bytes (bytes) | 1.3 MiB | 1.3 MiB | +1.6% | +| 64 B, checksum on | peak_rss_bytes (bytes) | 97.5 MiB | 98.0 MiB | +0.6% ~ | +| 1 KiB, checksum on | msgs_per_sec (msgs/s) | 217 | 220 | +1.3% ~ | +| 1 KiB, checksum on | mib_per_sec (MiB/s) | 0.2 | 0.2 | +1.3% ~ | +| 1 KiB, checksum on | alloc_bytes_per_op (B/op) | 5,513 | 5,633 | +2.2% | +| 1 KiB, checksum on | heap_after_cycle_bytes (bytes) | 1.3 MiB | 1.3 MiB | +1.6% | +| 1 KiB, checksum on | peak_rss_bytes (bytes) | 111.4 MiB | 111.9 MiB | +0.4% ~ | +| 1 KiB, checksum off | msgs_per_sec (msgs/s) | 208 | 201 | -3.2% ~ | +| 1 KiB, checksum off | mib_per_sec (MiB/s) | 0.2 | 0.2 | -3.2% ~ | +| 1 KiB, checksum off | alloc_bytes_per_op (B/op) | 5,513 | 5,633 | +2.2% | +| 1 KiB, checksum off | heap_after_cycle_bytes (bytes) | 1.3 MiB | 1.3 MiB | +1.6% | +| 1 KiB, checksum off | peak_rss_bytes (bytes) | 111.2 MiB | 111.8 MiB | +0.5% ~ | +| 8 KiB, checksum on | msgs_per_sec (msgs/s) | 202 | 201 | -0.5% ~ | +| 8 KiB, checksum on | mib_per_sec (MiB/s) | 1.6 | 1.6 | -0.5% ~ | +| 8 KiB, checksum on | alloc_bytes_per_op (B/op) | 41,353 | 41,473 | +0.3% | +| 8 KiB, checksum on | heap_after_cycle_bytes (bytes) | 1.3 MiB | 1.3 MiB | +1.5% | +| 8 KiB, checksum on | peak_rss_bytes (bytes) | 157.3 MiB | 148.9 MiB | -5.3% | +| 64 KiB, checksum on | msgs_per_sec (msgs/s) | 208 | 217 | +4.2% ~ | +| 64 KiB, checksum on | mib_per_sec (MiB/s) | 13.0 | 13.5 | +4.2% ~ | +| 64 KiB, checksum on | alloc_bytes_per_op (B/op) | 328,073 | 328,193 | +0.0% | +| 64 KiB, checksum on | heap_after_cycle_bytes (bytes) | 1.3 MiB | 1.4 MiB | +1.5% | +| 64 KiB, checksum on | peak_rss_bytes (bytes) | 379.5 MiB | 380.2 MiB | +0.2% ~ | + +## Round-trip latency (offer+poll) + +| Case | Metric | main | this branch | delta | +|---|---|---:|---:|---:| +| 1 KiB, checksum on | p50_us (us) | 8,692.0 | 8,576.8 | -1.3% ~ | +| 1 KiB, checksum on | p95_us (us) | 10,375.9 | 12,470.1 | +20.2% ~ | +| 1 KiB, checksum on | p99_us (us) | 11,827.3 | 17,036.0 | +44.0% ~ | +| 1 KiB, checksum on | alloc_bytes_per_op (B/op) | 8,786 | 8,954 | +1.9% | + +## Open+close on populated file (5,000 x 1 KiB messages) + +| Case | Metric | main | this branch | delta | +|---|---|---:|---:|---:| +| 1 KiB, checksum on | p50_us (us) | 8,634.8 | 9,364.5 | +8.5% ~ | +| 1 KiB, checksum on | p95_us (us) | 11,391.7 | 12,438.0 | +9.2% ~ | +| 1 KiB, checksum on | mean_us (us) | 8,991.4 | 9,700.3 | +7.9% ~ | + +`~` = within run-to-run noise; treat as equivalent. diff --git a/benchmarks/results/throughput.png b/benchmarks/results/throughput.png new file mode 100644 index 0000000..ecc292d Binary files /dev/null and b/benchmarks/results/throughput.png differ diff --git a/benchmarks/run_benchmarks.sh b/benchmarks/run_benchmarks.sh new file mode 100755 index 0000000..eb45537 --- /dev/null +++ b/benchmarks/run_benchmarks.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# Before/after benchmark driver for the persistent message queue. +# +# Compares the library on `main` (before) against this branch (after). +# Library sources for each side are exported with `git archive` into a +# scratch dir, so the working tree is never touched. The same harness +# (benchmarks/src) is compiled once against each side's classes and the +# same scenarios run against both, interleaved per trial to reduce +# machine-noise bias. +# +# Reuses the Temurin JDK that run_tests.sh downloads into target/jdk/. +# Results land in benchmarks/results/ (raw JSONL + CSV, summary.md, PNGs). +# +# Usage: benchmarks/run_benchmarks.sh +# TRIALS=5 (default) can be overridden via the environment. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +BENCH="$ROOT/benchmarks" +RESULTS="$BENCH/results" +WORK="${BENCH_WORK:-/tmp/pmqueue-bench}" +TRIALS="${TRIALS:-5}" +BEFORE_REF="${BEFORE_REF:-main}" +AFTER_REF="${AFTER_REF:-HEAD}" +JAVA_RELEASE=21 +JVM_FLAGS=(-Xms512m -Xmx512m) + +# --- JDK (same download logic as run_tests.sh) --------------------------- +JDK_DIR="$ROOT/target/jdk" +find_javac() { + find "$JDK_DIR" -type f -name javac -path '*/bin/*' 2>/dev/null | head -1 +} +JAVAC="$(find_javac || true)" +if [ -z "${JAVAC:-}" ]; then + case "$(uname -s)" in + Darwin) os=mac ;; + Linux) os=linux ;; + *) echo "unsupported OS: $(uname -s)" >&2; exit 1 ;; + esac + case "$(uname -m)" in + arm64|aarch64) arch=aarch64 ;; + x86_64) arch=x64 ;; + *) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;; + esac + echo "Downloading Temurin $JAVA_RELEASE JDK ($os/$arch) into $JDK_DIR ..." + mkdir -p "$JDK_DIR" + curl -fsSL -o "$JDK_DIR/jdk.tar.gz" \ + "https://api.adoptium.net/v3/binary/latest/$JAVA_RELEASE/ga/$os/$arch/jdk/hotspot/normal/eclipse" + tar -xzf "$JDK_DIR/jdk.tar.gz" -C "$JDK_DIR" + rm "$JDK_DIR/jdk.tar.gz" + JAVAC="$(find_javac)" +fi +[ -n "$JAVAC" ] || { echo "JDK setup failed" >&2; exit 1; } +JAVA="$(dirname "$JAVAC")/java" +"$JAVA" -version + +# --- Export and compile both sides --------------------------------------- +rm -rf "$WORK" +mkdir -p "$WORK/data" "$RESULTS" + +compile_side() { + side="$1" + ref="$2" + echo "Exporting library sources for '$side' ($ref) ..." + mkdir -p "$WORK/src-$side" + git -C "$ROOT" archive "$ref" src/main/java | tar -x -C "$WORK/src-$side" + echo "Compiling library for '$side' ..." + mkdir -p "$WORK/classes-$side" + find "$WORK/src-$side" -name '*.java' -print0 | xargs -0 "$JAVAC" \ + --release "$JAVA_RELEASE" -nowarn -d "$WORK/classes-$side" + echo "Compiling harness against '$side' classes ..." + mkdir -p "$WORK/bench-$side" + "$JAVAC" --release "$JAVA_RELEASE" -nowarn -cp "$WORK/classes-$side" \ + -d "$WORK/bench-$side" "$BENCH"/src/*.java +} + +compile_side main "$BEFORE_REF" +compile_side branch "$AFTER_REF" + +# --- Scenario matrix ------------------------------------------------------ +# scenario size ops warmup checksum +# poll() and the round trip fsync on every operation (library behavior on +# both sides), so those op counts are sized for ~200 ops/s on a laptop SSD. +SCENARIOS=( + "offer 64 50000 10000 true" + "offer 1024 50000 10000 true" + "offer 8192 10000 2000 true" + "offer 65536 2000 500 true" + "poll 64 1500 300 true" + "poll 1024 1500 300 true" + "poll 8192 1500 300 true" + "poll 65536 800 200 true" + "offer 1024 50000 10000 false" + "poll 1024 1500 300 false" + "latency 1024 400 80 true" + "openclose 1024 100 20 true" +) + +RAW="$RESULTS/raw.jsonl" +: > "$RAW" + +TIME_BIN=/usr/bin/time +HAVE_TIME_L=0 +if [ "$(uname -s)" = "Darwin" ] && [ -x "$TIME_BIN" ]; then + HAVE_TIME_L=1 +fi + +run_one() { + side="$1" trial="$2" scenario="$3" size="$4" ops="$5" warmup="$6" checksum="$7" + cp_arg="$WORK/bench-$side:$WORK/classes-$side" + rm -rf "$WORK/data" + mkdir -p "$WORK/data" + args=(scenario="$scenario" side="$side" trial="$trial" size="$size" \ + ops="$ops" warmup="$warmup" checksum="$checksum" datadir="$WORK/data") + if [ "$HAVE_TIME_L" = 1 ]; then + line="$("$TIME_BIN" -l "$JAVA" "${JVM_FLAGS[@]}" -cp "$cp_arg" bench.QueueBench "${args[@]}" \ + 2> "$WORK/time.out")" + rss="$(awk '/maximum resident set size/ {print $1}' "$WORK/time.out")" + line="${line%\}},\"peak_rss_bytes\":${rss:-0}}" + else + line="$("$JAVA" "${JVM_FLAGS[@]}" -cp "$cp_arg" bench.QueueBench "${args[@]}")" + fi + echo "$line" >> "$RAW" + echo " $side trial=$trial $scenario size=$size checksum=$checksum done" +} + +echo "Running $TRIALS trials x ${#SCENARIOS[@]} scenarios x 2 sides ..." +for trial in $(seq 1 "$TRIALS"); do + # alternate which side goes first each trial to reduce drift bias + if [ $((trial % 2)) -eq 1 ]; then + order=(main branch) + else + order=(branch main) + fi + for side in "${order[@]}"; do + for row in "${SCENARIOS[@]}"; do + # shellcheck disable=SC2086 + run_one "$side" "$trial" $row + done + done +done + +# --- Environment metadata ------------------------------------------------- +{ + echo "{" + echo " \"date\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"," + echo " \"os\": \"$(uname -sm)\"," + echo " \"jvm\": \"$("$JAVA" -version 2>&1 | head -1 | sed 's/"/\\"/g')\"," + echo " \"jvm_flags\": \"${JVM_FLAGS[*]}\"," + echo " \"trials\": $TRIALS," + echo " \"before_ref\": \"$(git -C "$ROOT" rev-parse --short "$BEFORE_REF")\"," + echo " \"after_ref\": \"$(git -C "$ROOT" rev-parse --short "$AFTER_REF")\"" + echo "}" +} > "$RESULTS/env.json" + +# --- Aggregate + plots ------------------------------------------------------ +echo "Aggregating ..." +python3 "$BENCH/aggregate.py" "$RESULTS" + +VENV="${BENCH_VENV:-/tmp/benchvenv}" +if [ ! -x "$VENV/bin/python" ]; then + echo "Creating plot venv at $VENV ..." + python3 -m venv "$VENV" + "$VENV/bin/pip" -q install matplotlib +fi +if "$VENV/bin/python" -c "import matplotlib" 2>/dev/null; then + echo "Plotting ..." + "$VENV/bin/python" "$BENCH/make_plots.py" "$RESULTS" +else + echo "matplotlib unavailable; skipping plots" >&2 +fi + +echo "Done. Results in $RESULTS" diff --git a/benchmarks/src/QueueBench.java b/benchmarks/src/QueueBench.java new file mode 100644 index 0000000..c0396ec --- /dev/null +++ b/benchmarks/src/QueueBench.java @@ -0,0 +1,320 @@ +package bench; + +import java.io.File; +import java.io.IOException; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import io.github.elimelt.pmqueue.MessageQueue; +import io.github.elimelt.pmqueue.QueueConfig; +import io.github.elimelt.pmqueue.core.PersistentMessageQueue; +import io.github.elimelt.pmqueue.message.Message; + +/** + * Plain-Java benchmark harness for the persistent message queue. + * + * One JVM invocation runs one scenario for one trial and prints a single + * JSON line to stdout. The driver script (run_benchmarks.sh) handles trial + * interleaving between the two library builds and collects the output. + * + * Only APIs that exist on both sides under comparison are used: + * QueueConfig.Builder, new PersistentMessageQueue(QueueConfig), + * MessageQueue, and Message. + * + * Scenarios: + * offer timed offer loop (close included so buffered writes flush) + * poll timed poll loop over a pre-populated file + * latency single-message offer+poll round trips, percentiles reported + * openclose open+close cycles on an existing populated file + * + * Memory metrics captured per run: + * - bytes allocated per operation (per-thread allocation counter delta) + * - GC count and GC time deltas over the measured window + * - steady-state heap after a full produce/consume cycle (poll scenario) + */ +public final class QueueBench { + + // prevents dead-code elimination of polled payloads + private static long sink = 0; + + public static void main(String[] args) throws Exception { + Map a = new HashMap<>(); + for (String arg : args) { + int eq = arg.indexOf('='); + a.put(arg.substring(0, eq), arg.substring(eq + 1)); + } + + String scenario = a.get("scenario"); + String side = a.get("side"); + int trial = Integer.parseInt(a.get("trial")); + int size = Integer.parseInt(a.get("size")); + int ops = Integer.parseInt(a.get("ops")); + int warmup = Integer.parseInt(a.get("warmup")); + boolean checksum = Boolean.parseBoolean(a.get("checksum")); + String dataDir = a.get("datadir"); + + Result r; + switch (scenario) { + case "offer" -> r = benchOffer(dataDir, size, ops, warmup, checksum); + case "poll" -> r = benchPoll(dataDir, size, ops, warmup, checksum); + case "latency" -> r = benchLatency(dataDir, size, ops, warmup, checksum); + case "openclose" -> r = benchOpenClose(dataDir, size, ops, warmup, checksum); + default -> throw new IllegalArgumentException("unknown scenario: " + scenario); + } + + StringBuilder json = new StringBuilder(); + json.append('{'); + json.append("\"scenario\":\"").append(scenario).append('"'); + json.append(",\"side\":\"").append(side).append('"'); + json.append(",\"trial\":").append(trial); + json.append(",\"size\":").append(size); + json.append(",\"checksum\":").append(checksum); + json.append(",\"ops\":").append(ops); + for (Map.Entry e : r.fields.entrySet()) { + json.append(",\"").append(e.getKey()).append("\":").append(e.getValue()); + } + json.append(",\"sink\":").append(sink % 1000); + json.append('}'); + System.out.println(json); + } + + private static final class Result { + final Map fields = new java.util.LinkedHashMap<>(); + + Result put(String k, Object v) { + fields.put(k, v); + return this; + } + } + + private static MessageQueue open(String path, boolean checksum) throws IOException { + return new PersistentMessageQueue(new QueueConfig.Builder() + .filePath(path) + .checksumEnabled(checksum) + .build()); + } + + private static byte[] payload(int size) { + byte[] data = new byte[size]; + new Random(42).nextBytes(data); + return data; + } + + private static void deleteFile(String path) { + new File(path).delete(); + } + + // --- memory helpers ---------------------------------------------------- + + private static long threadAllocatedBytes() { + com.sun.management.ThreadMXBean tb = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean(); + return tb.getThreadAllocatedBytes(Thread.currentThread().threadId()); + } + + private static long[] gcSnapshot() { + long count = 0; + long timeMs = 0; + for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { + long c = gc.getCollectionCount(); + long t = gc.getCollectionTime(); + if (c > 0) { + count += c; + } + if (t > 0) { + timeMs += t; + } + } + return new long[] { count, timeMs }; + } + + private static long settledHeapUsed() throws InterruptedException { + for (int i = 0; i < 3; i++) { + System.gc(); + Thread.sleep(150); + } + return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); + } + + // --- scenarios ---------------------------------------------------------- + + private static Result benchOffer(String dataDir, int size, int ops, int warmup, boolean checksum) + throws Exception { + byte[] data = payload(size); + + // warmup on a throwaway file + String wf = dataDir + "/warmup.queue"; + deleteFile(wf); + try (MessageQueue q = open(wf, checksum)) { + for (int i = 0; i < warmup; i++) { + q.offer(new Message(data, i)); + } + } + deleteFile(wf); + + String f = dataDir + "/offer.queue"; + deleteFile(f); + MessageQueue q = open(f, checksum); + + long[] gc0 = gcSnapshot(); + long alloc0 = threadAllocatedBytes(); + long t0 = System.nanoTime(); + for (int i = 0; i < ops; i++) { + if (!q.offer(new Message(data, i))) { + throw new IllegalStateException("offer rejected at op " + i); + } + } + q.close(); // flush buffered writes; part of the timed region + long t1 = System.nanoTime(); + long alloc1 = threadAllocatedBytes(); + long[] gc1 = gcSnapshot(); + deleteFile(f); + + long elapsed = t1 - t0; + return new Result() + .put("elapsed_ns", elapsed) + .put("msgs_per_sec", ops * 1e9 / elapsed) + .put("mib_per_sec", (double) ops * size * 1e9 / elapsed / (1024 * 1024)) + .put("alloc_bytes_per_op", (alloc1 - alloc0) / (double) ops) + .put("gc_count", gc1[0] - gc0[0]) + .put("gc_time_ms", gc1[1] - gc0[1]); + } + + private static Result benchPoll(String dataDir, int size, int ops, int warmup, boolean checksum) + throws Exception { + byte[] data = payload(size); + + // warmup: full produce/consume cycle on a throwaway file + String wf = dataDir + "/warmup.queue"; + deleteFile(wf); + try (MessageQueue q = open(wf, checksum)) { + for (int i = 0; i < warmup; i++) { + q.offer(new Message(data, i)); + } + } + try (MessageQueue q = open(wf, checksum)) { + for (int i = 0; i < warmup; i++) { + sink += q.poll().getData()[0]; + } + } + deleteFile(wf); + + // populate (not timed) + String f = dataDir + "/poll.queue"; + deleteFile(f); + try (MessageQueue q = open(f, checksum)) { + for (int i = 0; i < ops; i++) { + q.offer(new Message(data, i)); + } + } + + MessageQueue q = open(f, checksum); + long[] gc0 = gcSnapshot(); + long alloc0 = threadAllocatedBytes(); + long t0 = System.nanoTime(); + for (int i = 0; i < ops; i++) { + Message m = q.poll(); + if (m == null) { + throw new IllegalStateException("queue empty at op " + i); + } + sink += m.getData()[0]; + } + long t1 = System.nanoTime(); + long alloc1 = threadAllocatedBytes(); + long[] gc1 = gcSnapshot(); + + // steady-state heap after a full produce/consume cycle, queue still open + long heapUsed = settledHeapUsed(); + q.close(); + deleteFile(f); + + long elapsed = t1 - t0; + return new Result() + .put("elapsed_ns", elapsed) + .put("msgs_per_sec", ops * 1e9 / elapsed) + .put("mib_per_sec", (double) ops * size * 1e9 / elapsed / (1024 * 1024)) + .put("alloc_bytes_per_op", (alloc1 - alloc0) / (double) ops) + .put("gc_count", gc1[0] - gc0[0]) + .put("gc_time_ms", gc1[1] - gc0[1]) + .put("heap_after_cycle_bytes", heapUsed); + } + + private static Result benchLatency(String dataDir, int size, int ops, int warmup, boolean checksum) + throws Exception { + byte[] data = payload(size); + String f = dataDir + "/latency.queue"; + deleteFile(f); + MessageQueue q = open(f, checksum); + + for (int i = 0; i < warmup; i++) { + q.offer(new Message(data, i)); + sink += q.poll().getData()[0]; + } + + long[] samples = new long[ops]; + long[] gc0 = gcSnapshot(); + long alloc0 = threadAllocatedBytes(); + for (int i = 0; i < ops; i++) { + long t0 = System.nanoTime(); + q.offer(new Message(data, i)); + Message m = q.poll(); + long t1 = System.nanoTime(); + sink += m.getData()[0]; + samples[i] = t1 - t0; + } + long alloc1 = threadAllocatedBytes(); + long[] gc1 = gcSnapshot(); + q.close(); + deleteFile(f); + + Arrays.sort(samples); + return new Result() + .put("p50_us", samples[(int) (ops * 0.50)] / 1e3) + .put("p95_us", samples[(int) (ops * 0.95)] / 1e3) + .put("p99_us", samples[(int) (ops * 0.99)] / 1e3) + .put("max_us", samples[ops - 1] / 1e3) + .put("mean_us", Arrays.stream(samples).average().orElse(0) / 1e3) + .put("alloc_bytes_per_op", (alloc1 - alloc0) / (double) ops) + .put("gc_count", gc1[0] - gc0[0]) + .put("gc_time_ms", gc1[1] - gc0[1]); + } + + private static Result benchOpenClose(String dataDir, int size, int cycles, int warmup, boolean checksum) + throws Exception { + byte[] data = payload(size); + int populate = 5000; + + String f = dataDir + "/openclose.queue"; + deleteFile(f); + try (MessageQueue q = open(f, checksum)) { + for (int i = 0; i < populate; i++) { + q.offer(new Message(data, i)); + } + } + + for (int i = 0; i < warmup; i++) { + open(f, checksum).close(); + } + + long[] samples = new long[cycles]; + for (int i = 0; i < cycles; i++) { + long t0 = System.nanoTime(); + MessageQueue q = open(f, checksum); + q.close(); + long t1 = System.nanoTime(); + samples[i] = t1 - t0; + } + deleteFile(f); + + Arrays.sort(samples); + return new Result() + .put("p50_us", samples[(int) (cycles * 0.50)] / 1e3) + .put("p95_us", samples[(int) (cycles * 0.95)] / 1e3) + .put("p99_us", samples[(int) (cycles * 0.99)] / 1e3) + .put("mean_us", Arrays.stream(samples).average().orElse(0) / 1e3) + .put("populated_msgs", populate); + } +} diff --git a/docs/META-INF/MANIFEST.MF b/docs/META-INF/MANIFEST.MF deleted file mode 100644 index 2fe49f6..0000000 --- a/docs/META-INF/MANIFEST.MF +++ /dev/null @@ -1,4 +0,0 @@ -Manifest-Version: 1.0 -Created-By: Maven Javadoc Plugin 3.5.0 -Build-Jdk-Spec: 21 - diff --git a/docs/allclasses-index.html b/docs/allclasses-index.html index f90dfba..6e9ff9b 100644 --- a/docs/allclasses-index.html +++ b/docs/allclasses-index.html @@ -1,21 +1,25 @@ - -All Classes and Interfaces (pmqueue 1.0-SNAPSHOT API) + +All Classes and Interfaces - - + + - + -