This document analyzes the performance characteristics of query iterators in liblevenshtein-rust, focusing on the ordered query iterator after fixing two critical bugs.
Location: src/transducer/ordered_query.rs:126-197
Problem: Queries with large max_distance (e.g., 99) were only returning a subset of results.
Root Cause: The advance() method had a strict equality check distance == self.current_distance which caused results to be silently dropped when their actual distance (from infer_distance()) differed from their bucket's distance (from min_distance()).
Solution: Implemented re-queuing logic that moves intersections to the correct distance bucket when distance > current_distance.
Impact: All matching terms within max_distance are now correctly returned.
Location: src/transducer/ordered_query.rs:64-83, 126-197
Problem: Results at the same distance level were not properly sorted lexicographically.
Root Cause: VecDeque is FIFO - results came out in insertion order rather than lexicographic order. DAWG edges are iterated in sorted order, but items discovered at different tree depths don't maintain this ordering.
Solution:
sorted_buffer: Vec<OrderedCandidate> and buffer_index: usize fields to the structadvance() to collect all results at the current distance level, sort them by term, then yield them in orderImpact: Results now correctly satisfy the ordering guarantee: distance-first, then lexicographic.
OrderedQueryIterator)pub struct OrderedQueryIterator<N: DictionaryNode> {
pending_by_distance: Vec<VecDeque<Box<Intersection<N>>>>,
current_distance: usize,
max_distance: usize,
query: Vec<u8>,
algorithm: Algorithm,
state_pool: StatePool,
substring_mode: bool,
sorted_buffer: Vec<OrderedCandidate>,
buffer_index: usize,
}
advance() method (lines 126-197):
next() iterationsorted_buffer.sort_by(|a, b| a.term.cmp(&b.term))intersection.term()queue_children() method (lines 199-212):
transition_state_pooled() - likely expensiveBuffer Sorting Overhead:
advance()sorted_buffer.sort_by(|a, b| a.term.cmp(&b.term))Term Materialization:
advance()intersection.term() - reconstructs the full term string from PathNode chainBox Allocations:
queue_children()Box::new(PathNode::new(...)) (line 196)Box::new(Intersection::with_parent(...)) (line 199)State Transitions:
queue_children()transition_state_pooled()Distance Bucket Requeuing:
advance()Ordered Query:
Unordered Query:
Profile Buffer Sorting:
sort_bybench_ordered_query_sorting_overhead in query_iterator_benchmarks.rsOptimize Term Materialization:
intersection.term() costReduce Allocations:
Early Buffer Optimization:
Buffer Capacity Hints:
SIMD Optimizations:
benches/query_iterator_benchmarks.rsComprehensive criterion benchmarks covering:
benches/query_profiling.rsFlamegraph-focused benchmarks for identifying hotspots:
tests/query_comprehensive_test.rs19 comprehensive tests covering:
Status: All 139 tests passing
The ordered query iterator has been successfully debugged and now correctly:
The primary potential bottleneck is the buffer sorting operation, which occurs once per distance level. Profiling is needed to determine if this is actually significant in practice, or if the state transition operations dominate the runtime.
The comprehensive benchmarks and tests provide a solid foundation for identifying and validating optimizations.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |