Optimize transition lookup in CompiledAutomaton.addTail - #16361
Optimize transition lookup in CompiledAutomaton.addTail#16361rajat315315 wants to merge 5 commits into
Conversation
Performance BenchmarksWe created and ran a JMH micro-benchmark
Testing
|
danmuzi
left a comment
There was a problem hiding this comment.
Thanks for your contribution! :)
I have a few questions and suggestions:
-
Could you run luceneutil benchmarks (e.g., the wildcard, fuzzy, or regexp tasks) to see whether this improvement shows up in end-to-end query performance? It would be good to confirm that this optimization matters for real queries.
-
How did you measure the “before” numbers? The benchmark and the optimization are in the same PR, so it would help to describe the exact steps—for example, did you revert the core change and then run the same benchmark?
-
The error margins look quite large (±26–30%). Could you rerun the benchmark with
@Fork(3)or more on a quiet machine and share the hardware and JDK details? -
One thing I was curious about is that the previous linear scan can exit early, so in theory it could be faster when a state has only a few transitions.
I ran a quick test locally using a modified benchmark with single-byte labels only and inputs that always reachaddTail(). The binary search was actually about 32% faster even with four transitions, and about 2.3× as fast with 64 transitions, so I did not observe a regression on my machine (JDK 26, Windows).
Still, it would be nice to include a small-transition case in the benchmark itself so that this is covered going forward.
|
I have run benchmarking on local machine to double check on real world queries. This benchmark evaluates the performance impact of replacing the linear scan in Benchmark Configuration
1. Throughput Comparison (Queries Per Second - QPS)Higher QPS represents better performance:
2. Latency Comparison (Percentiles in Milliseconds)Lower latency represents better performance:
3. JMH Micro-Benchmark Results (
|
Benchmark Parameter (numTransitions) |
Baseline (ops/ms) | Feature (ops/ms) (Optimized) | Performance Change |
|---|---|---|---|
| 5 (Small transition case) | 32,254.47 | 25,221.83 | -21.8% |
| 60 (Larger transition case) | 11,812.39 | 15,259.30 | +29.2% |
JMH Key Observations:
- Small Transition overhead: For a small number of transitions (e.g., 5), a simple linear scan is extremely fast and has very little overhead. The binary search setup (calculating midpoint, bounds checking, and pointer shifting) introduces more overhead than a tight 5-iteration loop, leading to a ~21.8% regression in micro-benchmarks.
-
Larger Transition scaling: As the transition count scales up (e.g., 60), linear search overhead becomes dominant, requiring up to 60 steps per search. Binary search reduces the complexity from
$O(N)$ to$O(\log N)$ , scaling efficiently to produce a +29.2% performance improvement.
4. Overall Analysis & Key Takeaways
- End-to-End QPS Improvements: Wildcard and Fuzzy query throughput on a realistic Wikipedia index improved dramatically, showing +35% to +60% QPS improvements.
- Drastic Latency Reduction: Median (P50) search latencies for Wildcard and Fuzzy queries dropped by -44% to -63%, while tail latencies (P90, P99) saw similar or even higher reductions.
- Micro-benchmarking Trade-Offs: JMH micro-benchmarking confirms the binary search provides a +29.2% throughput boost when there is a larger number of transitions. For very small automata (e.g., 5 transitions), linear scan remains slightly faster due to low constant overhead. However, for real-world queries where automata often contain dozens or hundreds of transitions, the binary search yields significant net benefits.
|
@danmuzi I think it is a better idea to keep a threshold of 8 or 10 for the number of transitions.. |
Fixes: #16360
Description:
Summary
This PR optimizes the transition lookup in
CompiledAutomaton.addTailby replacing the linear scan with a binary search, addressing aTODOcomment in the codebase.Proposed Changes
CompiledAutomaton.addTailwith a binary search over the transitions usingautomaton.getTransition(state, index, transition).Testing
TestCompiledAutomatonto verify correctness:./gradlew :lucene:core:test --tests "org.apache.lucene.util.automaton.TestCompiledAutomaton"