execute_range_query_pipeline's step-major loop (#581 stage E.2) holds every group's bucket_map simultaneously instead of one at a time. Underlying accumulator data (all_data) was already fully resident before either loop shape — this is just per-group index overhead, but it's now paid for all groups at once.
fn build_bucket_map(
buckets: &[crate::stores::TimestampedBucket],
) -> HashMap<u64, Vec<&dyn AggregateCore>> {
let mut bucket_map: HashMap<u64, Vec<&dyn AggregateCore>> = HashMap::new();
for ((start, _), bucket) in buckets {
bucket_map.entry(*start).or_default().push(bucket.as_ref());
}
bucket_map
}
Fix: sorted Vec<(u64, &dyn AggregateCore)> + binary search instead of HashMap. No hash-table overhead, more compact per group — the win compounds now that N are held at once. Trade O(1) hash lookup for O(log n) binary search in window_buckets_for_step/single_window.
Not urgent — no signal yet that real group cardinality makes this matter. Filed to track, not to block.
execute_range_query_pipeline's step-major loop (#581 stage E.2) holds every group'sbucket_mapsimultaneously instead of one at a time. Underlying accumulator data (all_data) was already fully resident before either loop shape — this is just per-group index overhead, but it's now paid for all groups at once.Fix: sorted
Vec<(u64, &dyn AggregateCore)>+ binary search instead ofHashMap. No hash-table overhead, more compact per group — the win compounds now that N are held at once. Trade O(1) hash lookup for O(log n) binary search inwindow_buckets_for_step/single_window.Not urgent — no signal yet that real group cardinality makes this matter. Filed to track, not to block.