This document summarizes benchmark results for the MSM (Move-Split-Merge) metric implementation, including the direct DP algorithm, lower-bound pruning, and hybrid trie-based search.
Test Environment:
| Series Length | Standard DP | Optimized DP | Speedup |
|---|---|---|---|
| 10 | ~565 ns | ~565 ns | 1.0× |
| 50 | ~19 µs | ~14 µs | 1.4× |
| 100 | ~77 µs | ~56 µs | 1.4× |
| 500 | ~2.94 ms | ~2.12 ms | 1.4× |
Key Findings:
| Series Length | Length LB | Euclidean LB | L1 LB | Combined LB |
|---|---|---|---|---|
| 10 | 2.0 ns | 11 ns | 10 ns | 12 ns |
| 50 | 2.0 ns | 40 ns | 35 ns | 42 ns |
| 100 | 2.0 ns | 80 ns | 75 ns | 107 ns |
| 500 | 2.0 ns | 424 ns | 400 ns | 446 ns |
Key Findings:
| LB Type | Search Time | Relative |
|---|---|---|
| Length Only | 143.66 ms | 1.00× |
| Euclidean | 136.69 ms | 0.95× |
| L1 | 135.23 ms | 0.94× |
| Combined | 136.12 ms | 0.95× |
Key Findings:
| Database Size | With LB | Without LB | Speedup |
|---|---|---|---|
| 100 series | 25.7 ms | 31.2 ms | 1.21× |
| 500 series | 145.9 ms | 154.1 ms | 1.06× |
| 1000 series | 287.9 ms | 312.6 ms | 1.09× |
Key Findings:
| Database Size | Brute Force + LB | Hybrid Index | Ratio |
|---|---|---|---|
| 100 series | 6.7 µs | 26.6 ms | 1:4000 |
| 500 series | 35.6 µs | 139.1 ms | 1:3900 |
Analysis:
This surprising result requires careful interpretation:
Brute Force with LB only computes lower bounds in the fast path. When the LB exceeds the threshold (most candidates for tight thresholds), no full MSM is computed.
Hybrid Index incurs overhead from:
When Brute Force Wins:
When Hybrid Index Wins:
Recommendation: Use brute-force with LB pruning for databases under 10,000 series. Consider hybrid indexing for larger databases or when prefix sharing is expected.
| Bins (K) | Search Time | Relative |
|---|---|---|
| 16 | 10.8 ms | 1.0× |
| 64 | 84.5 ms | 7.8× |
| 256 | 85.1 ms | 7.9× |
Key Findings:
Recommendation: Start with K=64 bins for balanced precision/performance. Use K=16 for approximate search where speed is critical.
| Operation | Complexity | Notes |
|---|---|---|
| MSM DP | O(mn) | m, n = series lengths |
| Length LB | O(1) | Just length comparison |
| Euclidean LB | O(min(m,n)) | Single pass, SIMD-friendly |
| Trie Search | O(m × k^d) | m = query length, k = alphabet, d = max distance |
| Hybrid Search | O(m × k^d + c × mn) | c = candidate count after pruning |
| Structure | Space | Notes |
|---|---|---|
| MSM DP | O(min(m,n)) | Optimized variant |
| Trie Index | O(N × L) | N = series count, L = avg length, with prefix sharing |
| Hybrid Index | O(N × L + N × L') | Additional storage for original floats |
// Use brute force with lower bounds
use liblevenshtein::time_series::{search_with_lb, MsmConfig};
let results = search_with_lb(&query, &database, threshold, &msm_config);
// Use hybrid search with euclidean lower bounds
use liblevenshtein::time_series::{HybridSearchIndex, QuantizationConfig, MsmConfig, LowerBoundType};
let mut index = HybridSearchIndex::new(quant_config, msm_config);
index.set_lower_bound_type(LowerBoundType::EuclideanOnly);
// ... insert series ...
let results = index.search_exact(&query, threshold);
// Use approximate search with verification on top-k
let candidates = index.search_approximate(&query, max_distance);
// Verify top-k candidates with exact MSM
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 |