xiangfu0 opened a new pull request, #19539:
URL: https://github.com/apache/pinot/pull/19539
Broker partition pruning currently revisits the filter tree and rehashes the
same literals for every candidate segment. This change prepares visited
predicates and reuses literal partition IDs within each pruning call, while
checking every segment's current partition set.
Reuse is keyed by partition-function implementation, name, partition count,
normalizer and original constructor configuration. Retaining the original
configuration also covers plugins that inherit the default null
`getFunctionConfig()` implementation. AND/OR/IN keep their evaluation order and
short-circuit behavior. Empty/singleton inputs use uncached evaluation; mixed
configurations reuse a lookup probe without allocating one key per segment.
## Benchmark
Independently packaged baseline `5771d6acea60cd72738116835111cf7c49965373`
and candidate `8b15fbc6561e39dca7c17d14aa172657a63913db`, with identical
benchmark source and bytecode. `BenchmarkPartitionPruning` measures the
**complete prune call**, including the returned set's allocation. It cycles
through 32 prebuilt requests; parsing, real ZNRecord metadata loading, and
independent result-set checks run outside measurement. Homogeneous metadata
uses Murmur/128; interleaved metadata alternates Murmur/128 and Murmur/256.
Queries combine the measured predicate with two unrelated time filters.
Apple M4 Pro, 24 GiB RAM, Homebrew OpenJDK 25.0.4, JMH 1.37, one benchmark
thread, fixed 1 GiB heap, `-XX:ActiveProcessorCount=4`, GC profiler. Serial
baseline/PR/PR/baseline order: two independent forks per case per revision,
each with five 1-second warmups and five 1-second measurements. For
4,096-segment EQ and IN128, a supplemental ABBA comparison adds two forks per
revision with five 2-second warmups and five 1-second measurements, for four
forks per revision on those two cases. Every measured iteration is retained;
preliminary pilots are excluded.
The following are means across the retained iterations. This
shared-workstation benchmark demonstrates local pruning time and allocation;
production broker CPU, QPS, and end-to-end latency require workload replay.
| Metadata | Segments | Predicate | Baseline µs/call | PR µs/call | Time
reduction | Allocated B/call, baseline → PR |
|---|---:|---|---:|---:|---:|---:|
| homogeneous | 1 | EQ | 0.031 | 0.030 | 2.6% | 128 → 128 |
| homogeneous | 1 | IN_128 | 0.902 | 0.884 | 2.0% | 2503 → 2503 |
| homogeneous | 1 | UNRELATED | 0.033 | 0.031 | 4.2% | 208 → 208 |
| homogeneous | 256 | EQ | 6.988 | 2.541 | 63.6% | 8432 → 680 |
| homogeneous | 256 | IN_128 | 297.725 | 40.575 | 86.4% | 671551 → 12356 |
| homogeneous | 256 | IN_16 | 62.454 | 8.585 | 86.3% | 125316 → 2528 |
| homogeneous | 256 | UNRELATED | 7.295 | 5.111 | 29.9% | 12416 → 12800 |
| homogeneous | 4096 | EQ | 117.634 | 59.751 | 49.2% | 132689 → 2088 |
| homogeneous | 4096 | IN_128 | 5597.190 | 599.838 | 89.3% | 10743264 →
120997 |
| homogeneous | 4096 | IN_16 | 933.724 | 178.656 | 80.9% | 2003928 → 24705 |
| homogeneous | 4096 | UNRELATED | 130.144 | 86.770 | 33.3% | 196801 →
197185 |
| interleaved | 256 | EQ | 6.430 | 7.225 | -12.4% | 8416 → 1000 |
| interleaved | 256 | IN_128 | 363.839 | 57.059 | 84.3% | 751245 → 17301 |
| interleaved | 256 | UNRELATED | 7.467 | 6.799 | 9.0% | 12416 → 13072 |
**Tradeoffs and measurement limits:** Interleaved EQ is 12.4% slower (6.430
→ 7.225 µs) while allocating 88.1% fewer bytes. Queries without a partition
predicate allocate an extra 384 B/call for homogeneous metadata, or 656 B/call
in the interleaved case. Singleton controls are approximately unchanged; their
small timing differences should not be read as wins. The baseline's
large-segment EQ/IN128 cases vary between JVM forks despite stable iterations
within each fork; the per-fork means below expose that variation rather than
implying precise confidence in the pooled percentages. The first run began with
a host load average of 19.25 after packaging. This task ran no concurrent
builds or benchmark JVMs.
<details>
<summary>Per-fork means (µs per complete pruning call)</summary>
| Metadata | Segments | Predicate | Baseline forks A / B / C / D | PR forks
A / B / C / D |
|---|---:|---|---|---|
| homogeneous | 1 | EQ | 0.030 / 0.032 | 0.030 / 0.030 |
| homogeneous | 1 | IN_128 | 0.894 / 0.910 | 0.881 / 0.887 |
| homogeneous | 1 | UNRELATED | 0.033 / 0.032 | 0.032 / 0.031 |
| homogeneous | 256 | EQ | 7.267 / 6.709 | 2.523 / 2.558 |
| homogeneous | 256 | IN_128 | 312.676 / 282.774 | 40.851 / 40.299 |
| homogeneous | 256 | IN_16 | 62.223 / 62.686 | 8.478 / 8.692 |
| homogeneous | 256 | UNRELATED | 7.226 / 7.364 | 4.822 / 5.400 |
| homogeneous | 4096 | EQ | 95.980 / 124.076 / 136.227 / 114.252 | 60.270 /
59.315 / 59.386 / 60.034 |
| homogeneous | 4096 | IN_128 | 3737.818 / 5998.606 / 6369.155 / 6283.183 |
597.865 / 604.576 / 599.421 / 597.491 |
| homogeneous | 4096 | IN_16 | 934.902 / 932.546 | 174.327 / 182.985 |
| homogeneous | 4096 | UNRELATED | 129.133 / 131.155 | 83.924 / 89.615 |
| interleaved | 256 | EQ | 6.601 / 6.259 | 7.224 / 7.225 |
| interleaved | 256 | IN_128 | 357.585 / 370.093 | 56.524 / 57.594 |
| interleaved | 256 | UNRELATED | 7.575 / 7.360 | 6.803 / 6.796 |
</details>
## Reproduce
Copy `BenchmarkPartitionPruning.java` into a separate worktree at the
baseline commit. Package each worktree independently with JDK 25:
```sh
./mvnw -pl pinot-perf -am package -Ppinot-fastdev -DskipTests \
-Dskip.npm -Dskip.installnodenpm -Denforcer.skip \
-Dassembly.skipAssembly=true -Dmaven.javadoc.skip=true
```
Run each group in each worktree in baseline/PR/PR/baseline order, with
distinct output filenames. Verify the packaged broker/perf jars match their
module target jars before running.
```sh
"$JAVA_HOME/bin/java" -Xmx1g \
-cp 'pinot-perf/target/pinot-perf-pkg/lib/*' org.openjdk.jmh.Main \
BenchmarkPartitionPruning -wi 5 -i 5 -w 1s -r 1s -f 1 -t 1 \
-p _metadata=HOMOGENEOUS -p _numSegments=256,4096 \
-p _predicate=EQ,IN_16,IN_128,UNRELATED \
-prof gc -foe true -jvmArgs '-Xms1g -Xmx1g -XX:ActiveProcessorCount=4' \
-rf json -rff results.json
```
For singleton controls, use `_numSegments=1` and
`_predicate=EQ,IN_128,UNRELATED`. For interleaved controls, use
`_metadata=INTERLEAVED`, `_numSegments=256`, and
`_predicate=EQ,IN_128,UNRELATED`. Repeat the additional ABBA comparison for
`_metadata=HOMOGENEOUS`, `_numSegments=4096`, `_predicate=EQ,IN_128`, and `-w
2s`.
## Validation
- 22 tests passed, zero failures/errors/skips:
`SinglePartitionColumnSegmentPrunerTest` (11 cases), `SegmentPrunerTest` (8),
`SegmentPartitionMetadataManagerTest` (3).
- Hash-count assertions cover reuse across distinct function objects and
interleaved configurations. Tests also cover hidden plugin config, normalizers,
raw-byte hashing, lazy errors in both cached/uncached paths, missing metadata,
refresh, and concurrent-query isolation.
- Broker/perf `spotless:apply`, `license:format`, `checkstyle:check`, and
`license:check` passed.
- Warning/deprecation-enabled reactor `test-compile` passed, with no
warnings in changed sources.
--
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]