xiangfu0 opened a new pull request, #19402:
URL: https://github.com/apache/pinot/pull/19402

   > **Stacked on #19380** (off-heap group-by key tables and result holders for 
SSE). Review only the last commit (`ffec9cdc12`); the first two commits are 
#19380. Will rebase once #19380 merges.
   
   ## What
   
   First per-function off-heap aggregation state on top of #19380: 
`DISTINCTCOUNTULL` / `DISTINCTCOUNTRAWULL` group-by keeps each group's 
UltraLogLog register array (`2^p` bytes, ~4.1KB at the default p=12) in pooled 
direct memory instead of one heap object per group. A 200K-group query carries 
~840MB of sketch heap per segment execution today; with `groupByOffHeap` 
enabled that moves off the heap entirely.
   
   ## How
   
   - **New seam** 
`AggregationFunction#createOffHeapGroupByResultHolder(initialCapacity, 
maxCapacity)` (default `null` = unchanged). `DefaultGroupByExecutor` consults 
it first inside the existing off-heap gate and registers the returned holder on 
the `ResourceTrackingGroupKeyGenerator`, so the existing generator close sites 
release the memory. Later function conversions (t-digest, KLL, theta, distinct 
sets) reuse this hook.
   - **`OffHeapUltraLogLogGroupByResultHolder`**: append-only `groupKey -> 
slotId` indirection, so direct memory grows with the number of groups actually 
seen (like on-heap lazy allocation), never with the group-count upper bound. 
Slots live in 256KB pooled chunks (never moved/resized). The hash4j 0.30.0 
register-update math (`add`/`pack`/`unpack`) is vendored verbatim 
(`UltraLogLog` is final and heap-only) and pinned **byte-identical** to the 
library by a differential test across p=3/8/12/18/19.
   - **Two modes per holder**: raw input values hash straight into off-heap 
registers; dictionary-encoded input (dict-id bitmap) and pre-serialized-ULL 
BYTES input (incl. star-tree pre-aggregated columns) go through a lazy on-heap 
`ObjectGroupByResultHolder` delegate — mode exclusivity is enforced with a hard 
check. Untouched groups read back as `null`; extraction materializes a fresh 
heap copy per group.
   - **Plan-time `p` validation** (`[3, 26]`): previously only 
`UltraLogLog.create` checked the user-supplied literal; the off-heap holder 
sizes slots as `1 << p`, so an unchecked p could allocate up to 1GB per group 
(p=27..30) or corrupt neighbor slots via int-shift wrap (p>30). Minor behavior 
change: a query with an out-of-range p now fails at planning even when its 
filter matches zero rows.
   
   ## Benchmark (`BenchmarkOffHeapGroupByUllSSE`, new)
   
   2 segments x 2M rows, dict INT group column, raw LONG input, p=12, `-prof gc 
-wi 4 -w 5 -i 10 -r 5`, Xmx10g, M-series Mac:
   
   | benchmark | groups | on-heap ms/op | off-heap ms/op | latency | alloc 
MB/op | gc count/time per op |
   |---|---|---|---|---|---|---|
   | segmentGroupBy | 10K | 159.0 ± 3.7 | 140.0 ± 7.8 | **-12%** | 298 → 337 | 
15/36ms → 19/28ms |
   | segmentGroupBy | 200K | 348.0 ± 2.3 | 237.8 ± 18.3 | **-32%** | 1084 → 340 
| 27/264ms → 11/29ms |
   | query (full) | 10K | 180.1 ± 3.0 | 160.8 ± 0.8 | **-11%** | 694 → 855 | 
31/89ms → 42/63ms |
   | query (full) | 200K | 416.2 ± 3.9 | 360.7 ± 9.8 | **-13%** | 2312 → 2379 | 
63/815ms → 53/100ms |
   
   Off-heap wins latency in every cell. At 200K groups the segment-phase 
allocation drops 69% and GC time drops ~9x. The small-tier alloc increase 
(+13-23%) is extraction: off-heap materializes a 4KB heap copy per group at 
hand-off where on-heap returns the live object; the combine phase merges heap 
ULLs in both arms (off-heap combine is a later milestone).
   
   ## Testing
   
   - `OffHeapUltraLogLogGroupByResultHolderTest`: state-byte differential vs 
hash4j (incl. edge hashes, growth, chunk boundaries, wrapper-fallback arm via 
`setViewSizeLimitBytes(0)`), untouched-null / touch-empty semantics, delegate 
mode, INVALID_ID, close releases direct memory + idempotent, out-of-range p 
rejected.
   - `OffHeapGroupByQueriesTest#testDistinctCountULL` + 
`#testDistinctCountULLSerializedBytesAndStarTree`: on-heap vs off-heap 
differential over raw/dict/MV inputs, explicit p, RAWULL serialized output 
(byte-exact), filtered aggregation, order-by trim path, null handling, a 
serialized-ULL BYTES column, and a star-tree segment (asserted to actually 
serve the query) — every off-heap query asserts direct memory returns to 
baseline.
   - Full group-by battery (228 tests) green; spotless/checkstyle/license clean.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to