| Benchmark | Time (µs) | Notes |
|---|---|---|
fuzzy_multimap_query | 38.55 | Standard query with 5 values per set |
fuzzy_multimap_high_aggregation | 394.96 | High aggregation (10 values/set, distance=3) |
fuzzy_multimap_vec_concat | 38.13 | Vec concatenation instead of HashSet |
transducer_query_baseline | 46.69 | Baseline without aggregation |
dict_get_value_hashset | 1.20 | Dictionary value access (10 lookups) |
hashset_aggregation | 11.53 | Pure HashSet aggregation (50 sets) |
fuzzy_multimap_complete | 211.45 | Complete workflow (5 queries) |
query() method (CRITICAL)Location: src/cache/multimap.rs:258-279
Problem:
pub fn query(&self, query_term: &str, max_distance: usize) -> Option<C> {
// Step 1: Collect candidates into Vec
let candidates: Vec<_> = self.transducer
.query(query_term, max_distance)
.collect(); // ← FIRST ALLOCATION
if candidates.is_empty() {
return None;
}
// Step 2: Collect values into ANOTHER Vec
let values: Vec<C> = candidates
.into_iter()
.filter_map(|term| self.dictionary.get_value(&term))
.collect(); // ← SECOND ALLOCATION
if values.is_empty() {
return None;
}
// Step 3: Aggregate
Some(C::aggregate(values.into_iter()))
}
Impact:
Vec allocationsString candidates (expensive clones from transducer)Evidence:
fuzzy_multimap_query (38.55 µs) vs trans ducer_query_baseline (46.69 µs)fuzzy_multimap_high_aggregation (394.96 µs) shows quadratic behavior with more matchesProblem: Trans ducer returns owned String values which must be cloned during iteration.
Evidence:
Location: src/cache/multimap.rs:83-96
Problem:
fn aggregate<I>(values: I) -> Self
where
I: Iterator<Item = Self>,
{
values.fold(HashSet::new(), |mut acc, set| {
acc.extend(set); // ← Repeated rehashing for each set
acc
})
}
Impact:
hashset_aggregation: 11.53 µs for 50 setsextend() may trigger rehashingPotential Optimization:
reserve() to reduce rehashingEvidence: fuzzy_multimap_vec_concat (38.13 µs) ≈ fuzzy_multimap_query (38.55 µs)
Replace double-collect with single iterator chain:
pub fn query(&self, query_term: &str, max_distance: usize) -> Option<C> {
let mut values = self.transducer
.query(query_term, max_distance)
.filter_map(|term| self.dictionary.get_value(&term))
.peekable();
if values.peek().is_none() {
return None;
}
Some(C::aggregate(values))
}
Expected Impact: 15-25% reduction in allocation overhead
Add capacity hints to CollectionAggregate trait:
pub trait CollectionAggregate: Sized {
fn aggregate<I>(values: I) -> Self
where
I: Iterator<Item = Self>;
// NEW: Aggregate with size hint
fn aggregate_with_capacity<I>(values: I, capacity_hint: usize) -> Self
where
I: Iterator<Item = Self>,
{
Self::aggregate(values) // Default implementation
}
}
// HashSet implementation
impl<T: Eq + Hash + Clone> CollectionAggregate for HashSet<T> {
fn aggregate_with_capacity<I>(mut values: I, capacity_hint: usize) -> Self
where
I: Iterator<Item = Self>,
{
let mut acc = HashSet::with_capacity(capacity_hint);
for set in values {
acc.extend(set);
}
acc
}
}
Expected Impact: 10-20% reduction in aggregation time (especially for high aggregation)
Use SmallVec to avoid heap allocation for small result sets:
use smallvec::SmallVec;
// For queries that typically return <10 results
let candidates: SmallVec<[String; 8]> = self.transducer
.query(query_term, max_distance)
.collect();
Expected Impact: 5-10% improvement for small result sets (<10 matches)
Return an iterator-based view instead of eagerly collecting:
pub fn query_iter<'a>(&'a self, query_term: &str, max_distance: usize)
-> impl Iterator<Item = &'a C> + 'a
{
self.transducer
.query(query_term, max_distance)
.filter_map(move |term| self.dictionary.get_value(&term))
}
Expected Impact: Eliminates allocation for streaming use cases
Cache frequently-queried terms to avoid repeated string allocations.
Expected Impact: <5% for typical workloads
| Scenario | Current | Optimized | Improvement |
|---|---|---|---|
| Small queries (10-20 matches) | 38.55 µs | ~29 µs | 25% |
| High aggregation (100+ matches) | 394.96 µs | ~300 µs | 24% |
| Vec concatenation | 38.13 µs | ~30 µs | 21% |
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 |