Repository navigation
Conversation
The mutual recursion between insertNode and insertRecord gave every allocation and inserter callback a call stack that varied with tree depth and shape. The heap profiler keeps a bucket for each distinct stack and never frees it, so memory grew without bound with profiling enabled (golang/go#69590). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
This comment is from Claude Code, an AI agent, posted on behalf of @oschwald. I measured the impact of this PR at 1fc6a9f against Heap profilerThe claim holds. Test workload: load GeoLite2-City, overlay each GeoLite2-ASN network with
On main, each insert pass adds buckets, and the runtime never frees them. The new This only affects programs that link Correctness
PerformanceTime per operation. A plus sign means slower.
The end-to-end workload above, with profiling off, gave the same time on both sides. Every phase (load, overlay, cover, write) was within ±1.5% of main over 6 runs per side.
Allocations: I tried 2 changes to recover the walk cost. Neither helped:
I did not find the cause of the remaining walk cost. |
|
Before this gets merged, it would be worth trying to determine if we could eliminate the overhead of the change. Did you see a more significant impact when building databases yourself? The PR description doesn't include any observed impact. |
|
Thanks for the review, appreciated! I spent some time poking at the overhead, but I think it's mostly just inherent. The call stack is a more optimized path than a hand-written userspace stack, so even if we're doing something fairly similar, it takes more total instructions and has a little worse performance on these benchmarks.
Our use of For our use-case, however, the memory usage is really noticeable and we see periodic OOM kills without this change. |
The mutual recursion between insertNode and insertRecord resulted in a lot of unique and fairly deep call stacks. That's normally fine, except if you use the Go heap profiler, that ends up chewing through a lot of memory.
See golang/go#69590 for discussion about why having a lot of unique stacks results in high memory usage.
This PR rewrites the mutual recursion into iteration since any recursion can be implemented iteratively by simply keeping track of the stack yourself, and that keeps the go heap profiler happier.
As you can see from the commit co-author, this was AI assisted, but I also stand by this code as a human (i.e. I have reviewed it and am happy with it).
Summary by CodeRabbit