Skip to content

Releases: aalhour/fractaltree

v0.4.0: Unsorted Buffer Append with Lazy Sort

Choose a tag to compare

@aalhour aalhour released this 25 Apr 08:06
7a9c79b

What's Changed

Eliminates runtime.memmove (16% CPU) from the write path by replacing sorted buffer insertion with unsorted append and lazy sorting.

  • appendToBuffer switched from sorted insertion (O(B) memmove per insert) to O(1) unsorted append
  • Removed eager sorting from flushNode — sorting now only happens at leaf merge time via resolveMessages
  • Added linear-scan fallback in bufferMessagesForKey and bufferSlice for reads on unsorted buffers (safe under RLock)
  • Batch leaf merge and iterator refactor
  • Cross-impl benchmarks now match Google BTree node config (WithBlockSize(63))

Benchmarks (vs v0.3.0)

Benchmark Delta Meaning
Put (all sizes) ~ No change
Get/Hit +9.5% Slower (linear scan on unsorted buffer)
Get/Miss +5.6% Slower
Delete +11.1% Slower
Range/10 -99.7% Faster
Range/10000 -74.7% Faster
Upsert -15.8% Faster

Read regressions are expected and acceptable — this is a write-optimized B^ε-tree. The write path no longer pays any sorting or memmove cost on insert. Full
comparison in benchmarks/benchstat_v0.3.0...v0.4.0.txt.

Docs

  • ADR-004: design rationale, alternatives rejected, invariants

v0.3.0: Batch Leaf Merge

Choose a tag to compare

@aalhour aalhour released this 12 Apr 20:19
6c384c5

Performance Improvements

Three-path batch merge in applyToLeaf replaces per-message leafInsert/leafDelete loop:

Path When Improvement
Append fast-path Sequential inserts -20 to -26%
Binary-search + chunk-copy Interleaved puts -23 to -33%
In-place compaction Batches with deletes -53%

Geomean -13.9% across all benchmarks vs v0.2.1. Zero allocation regressions.

See docs/PERFORMANCE.md for full numbers and Google BTree comparison.

Documentation

  • docs/ADR.md — Architecture decision records for batch merge, size tracking, flush bucket reuse
  • docs/PERFORMANCE.md — Full benchmarks, cross-implementation comparison, reproduction instructions
  • CONTRIBUTING.md — Development workflow, testing practices, benchmark procedures
  • docs/ROADMAP.md — Completed work, profiling data, remaining optimizations

Design Details

See ADR-001 for the full decision record including alternatives benchmarked and rejected.

v0.2.1 — Reuse Flush Bucket Allocations

Choose a tag to compare

@aalhour aalhour released this 12 Apr 18:00
31c58d3

Performance Optimizations

P1: Reuse flush bucket allocations and compact buffer in-place

flushNode previously allocated a fresh [][]Message bucket slice and a remaining slice on every flush — producing 85.7% of all heap objects. This release:

  • Adds a reusable flushBuckets field to internal nodes, reset via [:0] each flush
  • Compacts the buffer in-place instead of allocating a new remaining slice

Benchmarks (vs v0.2.0, Apple M2 Max, Go 1.26, -count=6)

Benchmark Metric Before After Δ
Put/Sequential/1M sec/op 139ms 106ms -24%
Put/Random/1M sec/op 1.70s 1.08s -36%
Put/Random/1M B/op 5.5Gi 55Mi -99.0%
Put/Random/1M allocs 11.2M 4.8K -99.96%
Put/Random/100K B/op 200Mi 5.8Mi -97%
Mixed B/op 89Mi 21B -100%
Delete B/op 38.5Mi 3.3Ki -99.99%
Delete allocs 33K 6 -99.98%

Full benchstat comparison in benchmarks/benchstat_v0.2.0...v0.2.1.txt.

v0.2.0 — Performance Optimizations

Choose a tag to compare

@aalhour aalhour released this 12 Apr 17:44
7d0a0e8

Performance Optimizations

P0: Eliminate buffer scans in putLocked existence check

Replaced getFromNode (full root-to-leaf traversal with O(bufferCap) linear buffer scan per level) with existsInLeaf (leaf-only binary search) in the write path. A pendingDeletes counter triggers fallback to the full check only when buffered
deletes are in flight.

Benchmark improvements (Apple M2 Max, Go 1.26):

Benchmark Before After Δ
Put/Sequential/10K 1070µs 754µs -30%
Put/Random/10K 5.26ms 4.54ms -14%
Put/Sequential/100K 17.1ms 11.8ms -31%
Put/Random/100K 124ms 110ms -11%
Put/Sequential/1M 198ms 143ms -28%
Put/Random/1M 3.34s 1.85s -45%

Other changes

  • Fix BenchmarkDelete timer/setup issue (was rebuilding 100K tree inside timed loop)
  • Add benchstat to tooling dependencies via tools.go

v0.1.0

Choose a tag to compare

@aalhour aalhour released this 12 Apr 15:03
8dc58f2

fractaltree v0.1.0

Initial release of a write-optimized B-epsilon-tree (fractal tree) for Go.

Highlights

  • Generic ordered key-value store (cmp.Ordered or custom comparator)
  • Concurrent-safe with sync.RWMutex (read-many / write-one)
  • Buffered writes with configurable epsilon and block size
  • iter.Seq2 iterators: All, Ascend, Descend, Range, DescendRange
  • Stateful Cursor with Seek / Next / Prev
  • Upsert, PutIfAbsent, Increment, CompareAndSwap
  • DeleteRange for bulk key removal
  • DiskBETree with pluggable Flusher and Codec interfaces
  • Zero runtime dependencies
  • 97.8% test coverage, 159 tests, fuzz/stress/race clean
  • 15 runnable examples