feat: Ordered gap buffer + Fenwick tree, benchmarks, fix iterator deadlock#29
feat: Ordered gap buffer + Fenwick tree, benchmarks, fix iterator deadlock#29
Conversation
Ordered.Remove improves from O(N) to O(log N) amortized using a gap buffer with a Fenwick tree (binary indexed tree) for rank queries. At(i) and Index(m) change from O(1) to O(log N). Auto-compacts when gaps exceed 2x alive elements. Add comprehensive benchmarks: - bench_test.go: standard Go benchmarks for all 5 implementations - bench_stats_test.go: statistical benchmarks (BENCH_STATS=1) outputting CSV with min/max/avg/stddev/percentiles - bench_plot.py: generates per-operation PNG charts from CSV - bench_stats.csv: baseline results (660 records) - bench_charts/: 11 PNG charts Also fix zero-value style: use `var x T` instead of `x := 0`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Iterator, Ordered, and Backwards methods on Locked and LockedOrdered previously held a read lock for the entire duration of iteration. This caused a deadlock when the yield callback called any mutating method (Add, Remove, Clear, Pop) on the same set, since sync.RWMutex is not reentrant. Now these methods snapshot the elements under a read lock, release the lock, then iterate the snapshot. This eliminates the deadlock while preserving point-in-time consistency. The tradeoff is an O(n) allocation per iteration call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move bench_plot.py, bench_stats.csv, and bench_charts/ into a benchmarks/ subdirectory. Add a Benchmarks section to the README with instructions for running benchmarks and all 11 charts showing performance across implementations and set sizes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR rewrites the Ordered set implementation with a gap buffer + Fenwick tree (binary indexed tree) data structure to improve Remove from O(N) to O(log N), while keeping Add at O(1) amortized and Contains at O(1). It also fixes a deadlock in Locked/LockedOrdered iterator methods by switching to snapshot-based iteration, and adds comprehensive benchmarks.
Changes:
- Rewrite
Orderedinternals: replacevalues []Mwithslots []M,alive []bool,bit []int(Fenwick tree), andcount int. Add auto-compaction when gaps exceed 2× live elements. - Fix iterator deadlock in
Locked/LockedOrdered:Iterator,Ordered, andBackwardsmethods now collect a snapshot under read lock, then iterate without holding the lock. - Add comprehensive benchmark infrastructure: standard Go benchmarks (
bench_test.go), statistical benchmarks with CSV output (bench_stats_test.go), and a Python visualization script (bench_plot.py).
Reviewed changes
Copilot reviewed 9 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
ordered.go |
Rewrite internals with gap buffer + Fenwick tree; update all methods (Add, Remove, At, Index, Sort, Clear, Iterator, etc.) |
locked.go |
Switch Iterator to snapshot-based iteration to prevent deadlock |
locked_ordered.go |
Switch Iterator, Ordered, Backwards to snapshot-based iteration to prevent deadlock |
set.go |
Minor style change: i := 0 → var i int in Random function |
bench_test.go |
New standard Go benchmarks for all set implementations and operations |
bench_stats_test.go |
New statistical benchmark framework with CSV output, auto-calibration, and percentile computation |
bench_stats.csv |
Pre-generated benchmark results (661 lines) |
bench_plot.py |
Python script to generate per-operation charts from CSV data |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add TestOrdered_AtIndex_AfterRemoval: targeted test exercising At() and Index() after removals, verifying Fenwick tree consistency both before and after compaction. - Fix writeStatsCSV: check csv.Writer.Error() after Flush and propagate f.Close() errors instead of silently swallowing write failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 21 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Ordered rewrite: gap buffer + Fenwick tree
Removeimproved from O(N) to O(log N) amortized using a gap buffer with lazy compactionAtandIndexare now O(log N) via Fenwick tree (binary indexed tree) queriesAddandContainsremain O(1) amortized and O(1) respectivelyComprehensive benchmarks
bench_test.go) and statistical benchmarks (bench_stats_test.go)intandstringtypes, sizes 10 to 1M (10M behindBENCH_LARGE=1)benchmarks/bench_plot.py) with pre-generated charts inbenchmarks/bench_charts/Fix iterator deadlock in Locked/LockedOrdered
Iterator,Ordered, andBackwardsmethods previously held a read lock for the entire duration of iterationREADME updates
Test plan
-raceon macOStestSetConcurrencytests🤖 Generated with Claude Code