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]
