Releases: KentBeck/AdaptiveRadixTree2
Release list
v0.5.2 — bench harness expanded to three comparators
Changed (docs)
benchmarks.mdper-operation results now compare ART against three
implementations rather than one. Thebench/harness gained_Tidwall
(github.com/tidwall/btreev1.8.1, configured with
Options{NoLocks: true, Degree: 32}for parity withgoogle/btree's default
degree and to keep its sync.RWMutex out of the measurement) and_Plar
(github.com/plar/go-adaptive-radix-treev1.0.7, whose public API stores
interface{}values — the per-Put boxing alloc that costs is structural to
the API and is documented in-table) siblings ofPut,Get,GetMiss,
Delete, andRange. The Verdict prose has been rewritten to reflect the
new three-comparator landscape rather than the prior google/btree-only
framing. No core.gofiles were edited; the change is confined to the
nestedbench/module and thebenchmarks.mddoc.
v0.5.1 — refactor + CI/test fixes
Changed (internal)
- Deduplicated the four-way prefix-consume / terminal-check preambles in
get.go,delete.go, anditerate.gobehind three free helpers in
helpers.go(consumePrefix,terminalValue[V],yieldTerminalInRange[V]).
−40 net LOC. No additions to thenode/innerNodeinterfaces; every
helper is a free function over concrete types so no interface-method
indirection is introduced (runtime.getitabremains absent from Get and
Delete pprof profiles).put.gois intentionally left expanded — sharing
a helper across the prefix-split allocation boundary would reintroduce
interface dispatch on the hot path, per the reverted polymorphism spike
recorded inpolymorphism-failed.md. Hot-path benchmarks at parity with
v0.5.0 within run variance.
Fixed
artmap/ordered_test.go: theTestOrdered_Float64_OrderAcrossZerofixture
now usesmath.Copysign(0, -1)for actual IEEE-754 negative zero. The
prior-0.0literal was folded to+0.0by the parser (staticcheck
SA4026); the test now exercises the intended sign-bit case.
CI
- The fuzz-smoke workflow step now discovers
Fuzz*targets dynamically
across all packages and runs each one scoped to its own package. Replaces
ago test -fuzz=… ./...invocation that was rejected by Go with
"cannot use -fuzz flag with multiple packages" once theartmap
subpackage was introduced. Future fuzz targets in any package are picked
up automatically; an explicit empty-set guard fails the step if no
targets are discovered.
v0.5.0
Added
Tree[V]descending iteration and open-ended range methods:AllDescending,RangeFrom,RangeTo,RangeDescending.artmapsubpackage exposingOrdered[K cmp.Ordered, V any], a typed sorted-map façade over byte-keyed ART with order-preserving encoders for thecmp.Orderedtypes. Encoder overhead on int64 keys: +2.6 ns/op (Put), +2.5 ns/op (Get), +1.3 ns/op (Delete).art.LockedTree[V]: async.RWMutexwrapper exposingPut,Get,Delete,Len,Clear, andClone. UncontendedGetoverhead ~0.8 ns/op.
Performance
- De-parameterised the internal node interface so that
Vappears only onTree[V]and the leaf.Delete(bulk 10M, 8-byte keys): −39 ns/key (~34 % faster).Get(hit, 10M, 8-byte keys) showed a +8.5 ns/op regression at landing (d8df19e), but investigation (findings note2a29d2a7) attributed the gap to code-layout / i-cache sensitivity rather than the node-interface shape; the regression self-healed across subsequent layout-affecting commits. Re-measured at the release commit,Getis 43.68 ns/op — at parity with (slightly below) the pre-E-D2 baseline.
Documented
benchmarks.mdexpanded with three-column-per-engine tables (ns/op · B/op · allocs/op) and a key-shape sensitivity section coveringseqInt64/randInt64/uuid/urlPath.
Stability
- README now carries a
## Stabilitysection enumerating the exported surface of both packages that will be frozen at v1.0 and committing to Semantic Versioning from that tag forward. Target for v1.0.0 is no earlier than 2026-07-23.
v0.4.1 — performance recovery
Recovers the Get (hit) and Delete (bulk) regressions flagged in v0.4.0. Single-file refactor of put.go / delete.go / helpers.go; public API unchanged; Len remains O(1).
Performance
median of 5 reps, -benchtime=3s -count=5, VirtualApple @ 2.50 GHz, Go 1.24.2
| Operation | v0.4.0 | v0.4.1 | Δ |
|---|---|---|---|
| Get (hit) | 57.57 ns/op | 44.81 ns/op | fully recovered to pre-generics baseline (43.22 ns/op) |
| Delete (bulk) | 124.5 ns/key | 115.3 ns/key | −9 ns/key (~7 %); residual ~45 ns/key gap vs pre-generics (70.0 ns/key) tracked for follow-up |
ART is now ~21× faster than google/btree on Get (hit), up from 15× at v0.4.0.
What changed
Tree.sizeis now tracked at theinsertLeaf/clearTerminalIfMatcheschokepoints instead of being propagated through recursive return tuples. Parent frames detect no-op by pointer equality on the returned subtree.putInto/putIntoNodeN/deleteFromrevert to single-return-value signatures.- The Get recovery is a code-layout / register-pressure side effect of the put/delete refactor — no change to
get.go.
Documented
benchmarks.mdre-baselined at commitd14c9c6. Regression annotations removed / adjusted.CHANGELOG.mdbackfilled with entries for v0.3.0, v0.4.0, v0.4.1.
Install
go get github.com/KentBeck/AdaptiveRadixTree2@v0.4.1
Compare: v0.4.0...v0.4.1
v0.4.0 — benchmark re-baseline
Re-measured all 10 benchmarks at -benchtime=3s -count=5 against current HEAD. benchmarks.md now reflects post-generics reality with a documented protocol / environment footer.
Headline numbers
median of 5 reps, VirtualApple @ 2.50GHz, Go 1.24.2
| Operation | ART | B-tree | Winner |
|---|---|---|---|
| Put (bulk) | 150.8 ns/key | 752.6 ns/key | ART 5.0× |
| Get (hit) | 57.57 ns/op | 861.9 ns/op | ART 15× |
| Get (miss) | 9.36 ns/op | 118.5 ns/op | ART 12.7× |
| Delete (bulk) | 124.5 ns/key | 796.3 ns/key | ART 6.4× |
| Range (1 %, 100 K) | 19.21 ns/key | 10.73 ns/key | B-tree 1.8× |
Flagged for follow-up (regressions vs pre-generics)
- ART Get (hit) +33 %
- ART Delete +78 %
Both deferred — investigation is a separate PR.
Surprise improvement
- ART build-time allocs/entry dropped 2.02 → 1.02 (−50 %), likely a monomorphisation benefit from the generics refactor.
Install
go get github.com/KentBeck/AdaptiveRadixTree2@v0.4.0
Compare: v0.3.0...v0.4.0
v0.3.0 — CI, packaging, and corpus
Publishability polish.
Added
- GitHub Actions workflow (
.github/workflows/test.yml): matrix on Go 1.23.x / 1.24.x with build, vet, gofmt, staticcheck, race tests, and 30 s fuzz smoke. - Nested
bench/module: consumer dep graph no longer pulls ingithub.com/google/btree. Rootgo.modhas zero third-party deps. - Fuzz corpus:
testdata/fuzz/FuzzSortedMap/— 12 hand-authored seed entries covering promotions, demotions, prefix split/merge, empty key,Clone, andCeiling/Floormid-prefix. CHANGELOG.md(Keep a Changelog) with v0.1.0 and v0.2.0 history.- README: CI badge and CHANGELOG link.
Removed
- Four unused
isEmpty()methods and their interface declaration (staticcheckU1000).
Install
go get github.com/KentBeck/AdaptiveRadixTree2@v0.3.0
Compare: v0.2.0...v0.3.0
v0.2.0 — sorted-map surface
Adds sorted-map query operations and structural helpers.
Added
Min()— smallest (key, value, ok).Max()— largest (key, value, ok).Ceiling(target)— successor (smallest key ≥ target).Floor(target)— predecessor (largest key ≤ target).Clone()— independent structural copy.Clear()— O(1) remove-all.- Six new
ExampleTree_*functions (12 total).
Documented
- Nil-key / empty-key equivalence contract across
doc.go,Put,Get,Delete.
Tests
- +17 unit tests (92 total).
- Fuzzer extended with
Min/Max/Ceiling/Floor/Clone/Clearoperations, cross-checked against a sorted oracle.
Install
go get github.com/KentBeck/AdaptiveRadixTree2@v0.2.0
Compare: v0.1.0...v0.2.0
v0.1.0 — initial release
Adaptive Radix Tree as a sorted map from []byte to V.
Highlights
- Generic
Tree[V any]public API (New,Put,Get,Delete,Len,All,Range). - O(k)
Put/Get/Delete, O(1)Len. - Sorted iteration via Go 1.23 range-over-func (
All,Range). - Path compression with prefix splitting and terminal-carrying collapse.
- Inline small-key buffer (≤ 24 bytes) to halve
Putallocations. - Adaptive node sizing (node4 / node16 / node48 / node256) with promotion and demotion.
- Differential fuzzer against
map[string]V+ sorted oracle — 45M+ execs, zero divergences. - Mutation testing efficacy 96.55 % (100 % of killable mutants).
- Six verified
ExampleTree_*functions. - Package documentation (
doc.go) and goroutine-safety contract onTree.
Concurrency
A Tree is not safe for concurrent use by multiple goroutines when any goroutine is writing. Guard with your own sync.RWMutex if needed.
Install
go get github.com/KentBeck/AdaptiveRadixTree2@v0.1.0