Repository navigation
Conversation
TopKHeap::estimated_memory_bytes(heap_size, key) gives the bytes a heap holds once heap_size keys shaped like key are resident: the struct, the HeapEntry and digest arrays, the digest index (std HashMap layout on the build target) and each resident's owned key bytes. CountMinSketchWithHeap and CountSketchWithHeap over Vector2D add their rows x cols counters, so a memory model prices a top-k sketch before building one. Evictions can make the index rehash into twice its buckets, which the count leaves out.
|
Closing as superseded by #242, merged after this PR was opened. Checked on Not carried over from the review of this PR, and left as possible follow-ups on
The branch is kept. |
Why
A planner prices a top-k sketch before it builds one, and today it has to guess the heap's bytes. A
TopKHeapresident takes more than its count: a 48-byteHeapEntry<f64>, an 8-byte digest, a digest-index entry and the key's own bytes. None of the heap types say what they hold, so every caller re-derives the layout and drifts from it. ASAPPlanner charges 24 bytes per heap entry.This redoes #233 (closed with the
WeightedFrequencystack) onmain'sTopKHeapand its f64 heap sketches.What
TopKHeap::<C>::estimated_memory_bytes(heap_size, key): the bytes a heap of capacityheap_sizeholds onceheap_sizekeys shaped likekeyare resident.CountMinSketchWithHeap::<Vector2D<T>, M, H>::estimated_memory_bytes(rows, cols, heap_size, key)and the same onCountSketchWithHeap: the heap's bytes plus therows x colscounters and the struct.Behavior is otherwise unchanged.
How
The heap's count adds up these parts, using the types' own
size_of:HeapEntry<C>array and the parallelu64digest array, which reservemin(heap_size, 1024)up front and then grow by std's doubling;HashMapbuckets for that reservation, grown as a fresh fill grows them, plus control bytes and the trailing group, using the build target's group width;The heap sketches add
rows * cols * size_of::<T>()for their counters.Vector2Dallocates exactly that.Left out, and stated in the docs: evictions leave index tombstones that can make the index rehash into twice its buckets. Measured on a
5 x 4096Count-Min with 9-byte keys after20 x kdistinct updates:kWhether the rehash happens depends on where the tombstones land.
Key code interfaces added / modified
src/common/heap.rssrc/sketches/countminsketch_heap.rs,src/sketches/countsketch_heap.rs(the same onCountSketchWithHeap)Usage, end to end: a planner prices one group's state for
topk(100, sum by (region, port) (bytes_rate))before choosing a plan. The numbers come from running these calls at the head commit.Before this PR
A caller had no way to ask what a heap or a heap sketch holds, and guessed. ASAPPlanner priced 24 bytes per heap entry. The heap above holds about 130 bytes per resident.
After this PR
estimated_memory_bytesreturns the bytes the state holds once its heap is full, before the state exists. ASAPPlanner's plan selection can call it instead of hardcoding a per-entry size once its pin includes this PR.Verification
tests/heap_memory_bytes.rs(new; it failed to compile before the functions existed) checks the estimates againstsize_ofand against the bytes actually allocated, counted by a global allocator:TopKHeap<f64>filled withku64 keys andTopKHeap<i64>filled withk9-byte keys, fork∈ {1, 3, 7, 8, 100, 1024, 2000, 5000}. These cover both sides of the 1,024-slot reservation.CountMinSketchWithHeap(3 x 64) andCountSketchWithHeap(5 x 64) over f64, filled withk9-byte keys, fork∈ {1, 7, 64, 100, 1000}.cargo fmt --all -- --check: clean.cargo clippy --workspace --all-targets --locked -- -D warnings, with and without--all-features: clean.cargo test --lockedandcargo test --all-features --locked: all pass.HashMap's table layout on the build target (16-byte control groups with SSE2 on x86, 8 elsewhere). A std change to that layout would show up as a failure of this test.