executor: Improve HashAgg string collation key caching#10978
executor: Improve HashAgg string collation key caching#10978ChangRui-Ryan wants to merge 3 commits into
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
📝 WalkthroughWalkthroughThe PR adds CI collation sort-key caching to string hash aggregation, tracks persistent fallback after cache thresholds are exceeded, wires initialization and status propagation through ChangesCollation sort-key caching
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Aggregator
participant HashMethodString
participant Collator
participant AggregatedDataVariants
Aggregator->>AggregatedDataVariants: check fallback state
Aggregator->>HashMethodString: initialize cache for batch
HashMethodString->>Collator: generate or reuse sort keys
HashMethodString-->>Aggregator: return fallback status
Aggregator->>AggregatedDataVariants: persist fallback status
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
dbms/src/Common/ColumnsHashing.h (1)
102-141: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winReserve the lookup hash map here too.
cached_sort_keysis already reserved for the expected distinct count, butraw_to_sort_key_indexstill grows from its default capacity and will rehash repeatedly as the cache fills. Reserving it alongside the vector avoids avoidable rehashes.♻️ Proposed fix
collation_sort_key_cache_active = true; collation_sort_key_cache_distinct_limit = rows / max_collation_sort_key_cache_distinct_ratio; cached_sort_keys.reserve(collation_sort_key_cache_distinct_limit + 1); + raw_to_sort_key_index.reserve(collation_sort_key_cache_distinct_limit + 1);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dbms/src/Common/ColumnsHashing.h` around lines 102 - 141, Reserve raw_to_sort_key_index for collation_sort_key_cache_distinct_limit when initializing the sort-key cache, alongside the existing cached_sort_keys reservation, so filling the cache does not trigger repeated hash-map rehashes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@dbms/src/Common/ColumnsHashing.h`:
- Around line 102-141: Reserve raw_to_sort_key_index for
collation_sort_key_cache_distinct_limit when initializing the sort-key cache,
alongside the existing cached_sort_keys reservation, so filling the cache does
not trigger repeated hash-map rehashes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 06cf7ea1-eedb-4167-958d-12fb9dbf3da4
📒 Files selected for processing (6)
dbms/src/Common/ColumnsHashing.hdbms/src/Common/ColumnsHashingImpl.hdbms/src/Flash/tests/bench_aggregation_hash_map.cppdbms/src/Flash/tests/gtest_aggregation_executor.cppdbms/src/Interpreters/Aggregator.cppdbms/src/Interpreters/Aggregator.h
|
/retest |
3 similar comments
|
/retest |
|
/retest |
|
/retest |
|
@ChangRui-Ryan: The following tests failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
What problem does this PR solve?
Issue Number: close #10979
Problem Summary
For string
GROUP BYkeys using a case-insensitive collation such asutf8mb4_general_ci, HashAgg needs to generate a collation sort key before probing or inserting into the hash table.Previously, the sort key was generated for every input row. When the grouped column has a low number of distinct raw values, the same raw string is converted to the same sort key repeatedly, causing unnecessary collation processing and memory writes.
For example:
l_shipmodeusually has a very low NDV. If it usesutf8mb4_general_ci, repeatedly generating sort keys for millions of rows can consume a significant portion of the HashAgg execution time.At the same time, unconditionally caching sort keys is not suitable for high-NDV inputs because maintaining the cache may introduce additional hash lookups and memory usage without enough cache hits. Therefore, the optimization needs to provide substantial benefits for low-NDV workloads while keeping the high-NDV overhead small and bounded.
What is changed and how it works
This PR adds an always-on collation sort-key cache to the batch string-key processing path of HashAgg.
For each input Block, the cache maps a raw string value to its generated collation sort key:
When processing a row:
The cache is enabled only when all of the following conditions are satisfied:
To bound the cost for high-NDV inputs, the maximum number of cached distinct values is calculated from the number of rows in the current Block:
For a typical maximum runtime Block containing 65,536 rows, the cache can hold at most 2,048 distinct raw values.
If a cache miss occurs after the distinct-value limit has been reached, the current Block falls back to direct sort-key generation. The fallback state is also recorded in the worker's
AggregatedDataVariants, so subsequent Blocks processed by the same aggregation worker do not attempt to initialize the cache again. This worker-level once-fallback behavior prevents high-NDV workloads from repeatedly paying the cache construction and lookup overhead for every Block.The cache itself remains local to one Block. It only stores references to raw strings owned by that Block, so no input-column memory needs to be retained across Blocks. Only the fallback decision is preserved across Blocks.
The optimization does not change collation or grouping semantics. Hash-table keys are still the sort keys generated by the existing collator; the change only avoids regenerating an identical sort key for repeated raw string values.
The benchmark uses 128 Blocks with 65,536 rows per Block:
The results show substantial improvements for low-NDV and mixed workloads, while the worker-level fallback keeps the high-NDV overhead below 1% in the benchmark.
Check List
Tests
Side effects
Documentation
Release note
Summary by CodeRabbit
Summary
Performance
Bug Fixes
Tests