Repository navigation
fix(physical-plan): replace approximate map_size accounting with exac… - #25825
mohitgurav20 wants to merge 14 commits into
Conversation
…t HashTable::allocation_size() in GroupValuesPrimitive
…td::mem::size_of in test The test added in the exact HashTable accounting fix used std::mem::size_of::<(usize, u64)>(), but size_of is already in scope via use std::mem::size_of at the top of the file. This trips -D unused-qualifications on Linux CI (which runs with -D warnings), causing the linux build test job to fail. Use the bare size_of::<(usize, u64)>() to stay consistent with the import and satisfy the lint.
kosiew
left a comment
There was a problem hiding this comment.
Thanks for working on this. The allocation accounting change looks reasonable, but the new regression test currently fails because the map is already allocated by the constructor. Please fix the initial-state assertions so the focused test passes.
…test GroupValuesPrimitive::new() calls HashTable::with_capacity(128), which immediately reserves backing storage. The previous assertions treated the map as unallocated (capacity == 0, allocation_size == 0), causing the test to fail on the very first check. Instead, capture the actual nonzero initial allocation_size() returned by the constructor and assert that: - map.capacity() >= 128 (matching the with_capacity argument) - allocation_size() > 0 (pre-allocation is real) - size() == values.allocated_size() + initial_map_bytes This makes the focused regression test pass while still exercising the exact accounting that the broader fix (using allocation_size() instead of the naive capacity * size_of formula) is meant to validate.
|
Fixed — GroupValuesPrimitive::new() calls HashTable::with_capacity(128) which already allocates, so the initial-state assertions now capture that nonzero allocation and verify size() includes it. Pushed in 0ee0772. |
After replacing the naive capacity * size_of formula with HashTable::allocation_size() in fn size(), size_of is no longer used in production code. The module-level import then triggers an unused-imports warning that clippy -D warnings promotes to an error on the lib target. Move the import into mod tests where it is actually consumed, so the lib and test targets are both clean.
ArrayRef, DataType, EmitTo, and Arc are already imported in the parent module and brought into scope via 'use super::*'. This caused clippy to flag them as unused imports on the test target.
The previous commit removed ArrayRef, DataType, EmitTo, and Arc from mod tests under the incorrect assumption that use super::* re-exports private use declarations from the parent module. In Rust, glob imports only propagate pub items, so each of these must be imported explicitly. All four types are used directly in the test function bodies: - ArrayRef: type annotation on let arr bindings - DataType: passed to GroupValuesPrimitive::new(DataType::Int32) - EmitTo: EmitTo::First(n) and EmitTo::All arm in emit() calls - Arc: Arc::new(...) wrapping Int32Array values Removing them caused a compile error on the test target.
…pill.slt The exact spill count depends on when the memory budget is crossed, which shifts with any improvement to memory accounting precision. Switching GroupValuesPrimitive::size() from the naive capacity * size_of formula to HashTable::allocation_size() reports a slightly larger (more accurate) figure, which moves the spill trigger and changes the per-query spill count. The test comment already states 'spill_count metric must be > 0': the intent is to verify that spilling happened, not to pin to a specific count. Use spill_count=<slt:ignore> to accept any positive value, consistent with how Case G already handles its non-deterministic counts.
|
@mohitgurav20 |
…accounting-primitive # Conflicts: # datafusion/physical-plan/src/aggregates/group_values/single_group_by/primitive.rs
…explain_analyze.slt
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #25825 +/- ##
=======================================
Coverage 82.75% 82.75%
=======================================
Files 1147 1147
Lines 449841 449892 +51
Branches 449841 449892 +51
=======================================
+ Hits 372253 372300 +47
- Misses 54912 54915 +3
- Partials 22676 22677 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…misses in primitive test
|
@kosiew Conflicts are resolved against latest Summary of the updates:
All CI checks are passing now. Ready for another review whenever you have time! |
…accounting-primitive
a3cac01 to
184cc63
Compare
Which issue does this PR close?
Closes #25734
Related to #25188
Rationale for this change
GroupValuesPrimitive::size()was incorrectly estimating its hash-table allocation asmap.capacity() * size_of::<(usize, u64)>(). This naive calculation omittedhashbrown's control bytes and trailing group allocation.As a result, the reported memory size was smaller than the actual retained allocation (which is especially problematic for small tables). This PR corrects the retained-capacity accounting by utilizing
self.map.allocation_size()directly, just likeArrowBytesMap::size().What changes are included in this PR?
GroupValuesPrimitive::size()withself.map.allocation_size().test_exact_hash_table_allocation_accountingtoprimitive.rs.map.allocation_size()across empty, grown, and retained-after-emit map capacity states.Are these changes tested?
Yes, added the
test_exact_hash_table_allocation_accountingunit test insideprimitive.rs.Are there any user-facing changes?
No API changes. This strictly improves the internal memory-pool accounting for primitive single-column grouping.