Archive Date: 2025-11-11 Reason: Replaced by SmallVec implementation for superior performance Original Commit: d80c0df (feat: Universal Levenshtein BTreeSet optimization)
After comprehensive benchmarking comparing BTreeSet vs SmallVec approaches for Universal Levenshtein transducers, we discovered that SmallVec outperforms BTreeSet in 75% of scenarios with an average speedup of 1.08× (up to 2.06× faster).
See the full analysis at: docs/research/universal-levenshtein/UNIVERSAL_BTREESET_VS_SMALLVEC_RESULTS.md
The BTreeSet implementation used a custom Ord implementation that sorted positions by (errors, offset) to enable error-based early termination during subsumption checks.
take_while() to skip positions with fewer errors during subsumptionpub struct UniversalState<V: PositionVariant> {
positions: BTreeSet<UniversalPosition<V>>,
max_distance: u8,
}
pub fn add_position(&mut self, pos: UniversalPosition<V>) {
let pos_errors = pos.errors();
// Step 1: Remove subsumed (with early termination)
self.positions.retain(|p| {
if p.errors() <= pos_errors {
true // Cannot be subsumed
} else {
!subsumes(&pos, p, self.max_distance)
}
});
// Step 2: Check if subsumed (with early termination)
let is_subsumed = self.positions.iter()
.take_while(|p| p.errors() < pos_errors) // Early exit!
.any(|p| subsumes(p, &pos, self.max_distance));
if !is_subsumed {
self.positions.insert(pos); // O(log n) + heap allocation
}
}
Example for n=5 positions:
Based on typical usage:
The BTreeSet approach would theoretically be better for:
However: In practice, Universal Levenshtein states rarely exceed 100 positions, and even at n=100, SmallVec is still 1.8× faster due to cache effects.
Algorithmic complexity isn't everything: SmallVec's O(n) operations beat BTreeSet's O(log n) due to:
Early termination has limits: The error-based early termination in BTreeSet helped, but couldn't overcome the fundamental overhead of tree operations.
Consistency matters: Using SmallVec for both parameterized and universal transducers simplifies the codebase and maintenance.
Measure, don't guess: We initially hypothesized BTreeSet would be faster. Benchmarks proved otherwise.
This implementation was part of a series of optimizations for Universal Levenshtein transducers:
state.rs: Complete BTreeSet implementation (commit d80c0df)docs/research/universal-levenshtein/UNIVERSAL_BTREESET_VS_SMALLVEC_RESULTS.mdNote: This implementation is preserved for historical reference and educational purposes. It demonstrates a valid optimization strategy that was superseded by empirical evidence favoring SmallVec.
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 |