From a740468c9f713188de2b024ee5e80648ab37f346 Mon Sep 17 00:00:00 2001 From: Alexander Yevsyukov Date: Sat, 4 Jul 2026 01:30:22 +0100 Subject: [PATCH 01/13] Bump version -> `1.0.0-SNAPSHOT-011` Co-Authored-By: Claude Fable 5 --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index 547e4a0..7970b0f 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -24,4 +24,4 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -extra.set("versionToPublish", "1.0.0-SNAPSHOT-010") +extra.set("versionToPublish", "1.0.0-SNAPSHOT-011") From 486d3d03babd4f4e013bb5e918339e5c985aef92 Mon Sep 17 00:00:00 2001 From: Alexander Yevsyukov Date: Sat, 4 Jul 2026 18:23:18 +0100 Subject: [PATCH 02/13] Add the Phase 6 validation & benchmark matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 6 (validation & release). Add the comparative JMH matrix in the raw-JMH `benchmarks-jvm` tier — our `LongLongMap`/`IntIntMap` versus boxed `HashMap` and the four specialist primitive-map libraries (fastutil, HPPC, Eclipse Collections, Agrona) — over lookup/insert/churn across sizes and dense/adversarial key sets, plus a load-factor sweep, a JOL retained-footprint report, a reproducibility runner (env metadata + fixed seeds + JSON), and the benchmarking / release-runbook docs. Measured: our maps retain 19/10 bytes per entry — 4.68x/7.30x less than boxed `HashMap` and ~1.9x/1.8x less than the specialist libraries — and stay flat from dense to adversarial keys where the competitors degrade ~5-6x. Absolute time numbers are deferred to a pinned-hardware run; Maven Central publication is documented as a human-gated runbook (irreversible; needs signing keys, a Portal token, and a non-SNAPSHOT version). The competitor libraries are benchmark-only and JVM-only, confined to the `jmh` source set and never a dependency of the published `elastic` module. A `@Setup` correctness gate rejects any miswired adapter before a number is taken. Co-Authored-By: Claude Opus 4.8 --- .agents/tasks/phase-6-validation-release.md | 117 ++++++++ README.md | 19 +- benchmarks-jvm/build.gradle.kts | 51 ++++ benchmarks-jvm/capture-env.sh | 59 ++++ benchmarks-jvm/results/README.md | 30 ++ .../results/m4max-indicative/environment.txt | 10 + .../results/m4max-indicative/footprint.md | 13 + .../results/m4max-indicative/footprint.tsv | 13 + .../results/m4max-indicative/lookup-hit-1m.md | 25 ++ benchmarks-jvm/run-matrix.sh | 43 +++ .../benchmark/jmh/CompetitorAdapters.kt | 264 ++++++++++++++++++ .../elastic/benchmark/jmh/FootprintReport.kt | 141 ++++++++++ .../benchmark/jmh/IntIntMatrixBenchmark.kt | 159 +++++++++++ .../benchmark/jmh/LoadFactorBenchmark.kt | 163 +++++++++++ .../benchmark/jmh/LongLongMatrixBenchmark.kt | 164 +++++++++++ .../spine/elastic/benchmark/jmh/MatrixKeys.kt | 121 ++++++++ docs/benchmarking.md | 194 +++++++++++++ docs/performance-goals.md | 29 ++ docs/project.md | 14 +- docs/publishing.md | 112 ++++++++ 20 files changed, 1737 insertions(+), 4 deletions(-) create mode 100644 .agents/tasks/phase-6-validation-release.md create mode 100755 benchmarks-jvm/capture-env.sh create mode 100644 benchmarks-jvm/results/README.md create mode 100644 benchmarks-jvm/results/m4max-indicative/environment.txt create mode 100644 benchmarks-jvm/results/m4max-indicative/footprint.md create mode 100644 benchmarks-jvm/results/m4max-indicative/footprint.tsv create mode 100644 benchmarks-jvm/results/m4max-indicative/lookup-hit-1m.md create mode 100755 benchmarks-jvm/run-matrix.sh create mode 100644 benchmarks-jvm/src/jmh/kotlin/io/spine/elastic/benchmark/jmh/CompetitorAdapters.kt create mode 100644 benchmarks-jvm/src/jmh/kotlin/io/spine/elastic/benchmark/jmh/FootprintReport.kt create mode 100644 benchmarks-jvm/src/jmh/kotlin/io/spine/elastic/benchmark/jmh/IntIntMatrixBenchmark.kt create mode 100644 benchmarks-jvm/src/jmh/kotlin/io/spine/elastic/benchmark/jmh/LoadFactorBenchmark.kt create mode 100644 benchmarks-jvm/src/jmh/kotlin/io/spine/elastic/benchmark/jmh/LongLongMatrixBenchmark.kt create mode 100644 benchmarks-jvm/src/jmh/kotlin/io/spine/elastic/benchmark/jmh/MatrixKeys.kt create mode 100644 docs/benchmarking.md create mode 100644 docs/publishing.md diff --git a/.agents/tasks/phase-6-validation-release.md b/.agents/tasks/phase-6-validation-release.md new file mode 100644 index 0000000..a1538e1 --- /dev/null +++ b/.agents/tasks/phase-6-validation-release.md @@ -0,0 +1,117 @@ +# Phase 6 — Validation & release + +**Status: IMPLEMENTED (validation harness + docs); release is human-gated.** +Branch `phase-6`. Green on JVM (the new benchmark module compiles and runs; the +footprint report and JMH matrix produce results and JSON). Part of the phased +[implementation plan](elastic-hashing-implementation-plan.md); see also +[`docs/benchmarking.md`](../../docs/benchmarking.md) and +[`docs/publishing.md`](../../docs/publishing.md). + +## Goal + +Publish with numbers that survive external scrutiny: the full comparative benchmark +matrix versus the standard library and the specialist primitive-map libraries, +memory footprint, reproducibility metadata, honest baseline-qualified positioning, +and the Maven Central publication runbook. + +## What shipped + +### The comparative matrix (`benchmarks-jvm`, raw JMH — JVM-only) + +The four competitor libraries are JVM-only, so the matrix lives in the raw-JMH tier, +never on the published `elastic` module. Verified coordinates, all on the Java 17 +toolchain: **fastutil 8.5.18**, **HPPC 0.10.0**, **Eclipse Collections 13.0.0** +(`-api` + impl), **Agrona 2.5.0**. + +- `CompetitorAdapters.kt` — a `LongLongCell` / `IntIntCell` interface presenting our + map, boxed `HashMap`, and the four competitors behind one monomorphic type, each + pre-sized **in its own units** (the Phase 1 fairness rule). Handles the per-library + quirks: fastutil's settable `defaultReturnValue`, HPPC's fixed-0 absent value, + Eclipse's `void put` / `removeKey` / `java.util.Map`-free API, Agrona's mandatory + `missingValue` sentinel and its `java.util.Map`-implementing `size` **property**. +- `LongLongMatrixBenchmark` / `IntIntMatrixBenchmark` — `@Param impl × size × dist` + over ops `lookupHit` (random-access), `lookupMiss`, `insertPresized`, + `insertGrowing`, `churn`. A **correctness gate in `@Setup`** (the hit-sum must equal + the key-sum) refuses to benchmark a miswired adapter. **No `iterate` op**: our + primitive-value maps expose no non-boxing traversal (DP-10 lean surface), so a fair + head-to-head cannot include it — the gap is a tracked follow-up. +- `LoadFactorBenchmark` — lookup cost vs load factor `{0.50…0.99}` for the three + competitors with a **settable** load factor (fastutil, HPPC, Agrona). Ours (fixed + `7/8`) and `HashMap` (`0.75`) cannot be *held* at an arbitrary load and are fixed + reference points; the ≈0.99 regime belongs to the paper structures, gated by their + own probe-count specs. Capacity is pinned at `2^20` and the fill count chosen to hit + the target load; every invocation does the same `2^20` lookups, so scores are + load-comparable. +- `MatrixKeys.kt` — the `DENSE` and `CLUSTERED` key sets (the latter mandated by the + fairness gate) and the fixed-seed shuffle. The `Int` `CLUSTERED` stride is milder + than the `Long` one because a hard-collision key set is not constructible in-range + for 32-bit keys at a million entries. + +### The footprint report (JOL) + +`FootprintReport.kt` (`main()` in the jmh source set, `./gradlew +:benchmarks-jvm:footprintReport`) measures the exact retained heap of every map, built +raw (no adapter wrapper), pre-sized in its own units at `n = 229_376` (the `7/8` load +of a `2^18` table), and writes Markdown + TSV. Deterministic; the `Long→Long` figure +reproduces the committed Phase 1 measurement exactly. + +### Reproducibility harness + +- `capture-env.sh` — JDK/OS/CPU/RAM and, critically, whether CPU frequency was pinned; + portable across macOS (dev) and Linux (CI). +- `run-matrix.sh