Skip to main content

drain_flow/intern_benchmark_harness/
mod.rs

1// src/intern_benchmark_harness/mod.rs
2
3/// A trait for abstracting string interning operations.
4///
5/// This trait allows for benchmarking different string interning strategies
6/// by providing a common interface for interning and resolving strings.
7pub trait StringInternerTrait {
8    /// The type representing an interned string symbol or reference.
9    ///
10    /// For no interning, this could be `String` itself or `Arc<String>`.
11    /// For the `string-interner` crate, this would be its `Symbol` type.
12    type Symbol: Clone + Eq + std::hash::Hash + std::fmt::Debug;
13
14    /// Interns a string slice and returns its symbolic representation.
15    ///
16    /// # Arguments
17    ///
18    /// * `s` - The string slice to intern.
19    ///
20    /// # Returns
21    ///
22    /// The interned representation of the string.
23    fn intern(&mut self, s: &str) -> Self::Symbol;
24
25    /// Resolves an interned symbol back to its original string value.
26    ///
27    /// This method is crucial for verifying correctness and for certain interning
28    /// patterns, although not all "no interning" strategies would strictly need
29    /// it for performance benchmarks.
30    ///
31    /// # Arguments
32    ///
33    /// * `symbol` - The interned symbol or reference to resolve.
34    ///
35    /// # Returns
36    ///
37    /// An owned `String` containing the original value corresponding to the symbol.
38    fn resolve(&self, symbol: &Self::Symbol) -> String;
39}
40
41use crate::drains::simple::INTERNER as SHARED_INTERNER; // Access the global interner
42use parking_lot::RwLock;
43use std::collections::hash_map::RandomState;
44use std::sync::Arc;
45use string_interner::backend::{BucketBackend, BufferBackend, StringBackend}; // Import backends
46use string_interner::DefaultSymbol;
47use string_interner::StringInterner; // Import RandomState
48
49// New crates for benchmarking
50use interned_string::{IString, Intern};
51use lasso::{Rodeo, Spur};
52// For intern_string v0.1.0, use Intern and InternId
53use intern_string::{Intern as InternStringIntern, InternId as InternStringInternId};
54// For arc-string-interner
55use arc_string_interner::StringInterner as ArcStringInternerImpl;
56use arc_string_interner::Sym as ArcSym;
57
58/// An implementation of `StringInternerTrait` using the project's shared `string-interner`.
59///
60/// This struct provides a way to interact with the global, shared string interner
61/// (`simple::INTERNER`) for benchmarking purposes.
62pub struct SharedStringInterner {
63    /// A reference to the global `StringInterner` instance.
64    interner_arc: Arc<RwLock<StringInterner<string_interner::backend::BucketBackend>>>,
65}
66
67impl SharedStringInterner {
68    /// Creates a new `SharedStringInterner` instance.
69    ///
70    /// This constructor clones the `Arc` to the global interner, allowing multiple
71    /// `SharedStringInterner` instances to share the same underlying interner.
72    ///
73    /// # Returns
74    ///
75    /// A new `SharedStringInterner` instance.
76    pub fn new() -> Self {
77        Self {
78            interner_arc: SHARED_INTERNER.clone(),
79        }
80    }
81}
82
83impl StringInternerTrait for SharedStringInterner {
84    type Symbol = DefaultSymbol;
85
86    /// Interns a string slice using the shared `string-interner`.
87    ///
88    /// This method acquires a write lock on the shared interner to perform the
89    /// interning operation.
90    ///
91    /// # Arguments
92    ///
93    /// * `s` - The string slice to intern.
94    ///
95    /// # Returns
96    ///
97    /// The `DefaultSymbol` representing the interned string.
98    fn intern(&mut self, s: &str) -> Self::Symbol {
99        self.interner_arc.write().get_or_intern(s)
100    }
101
102    /// Resolves a `DefaultSymbol` back to its original string using the shared `string-interner`.
103    ///
104    /// This method acquires a read lock on the shared interner to perform the
105    /// resolution operation.
106    ///
107    /// # Arguments
108    ///
109    /// * `symbol` - The `DefaultSymbol` to resolve.
110    ///
111    /// # Returns
112    ///
113    /// An owned `String` corresponding to the resolved symbol.
114    ///
115    /// # Panics
116    ///
117    /// Panics if the symbol cannot be resolved, which indicates an inconsistency
118    /// (e.g., a symbol was created by an interner other than the shared one).
119    fn resolve(&self, symbol: &Self::Symbol) -> String {
120        let guard = self.interner_arc.read();
121        guard
122            .resolve(*symbol)
123            .expect("Symbol should exist in interner")
124            .to_owned()
125    }
126}
127
128// Implementations for the new string interning crates
129
130/// An implementation of `StringInternerTrait` using the `lasso` crate's `Rodeo` interner.
131pub struct LassoInterner {
132    interner: Rodeo,
133}
134
135impl LassoInterner {
136    /// Creates a new `LassoInterner` instance.
137    ///
138    /// # Returns
139    ///
140    /// A new `LassoInterner` instance.
141    pub fn new() -> Self {
142        Self {
143            interner: Rodeo::new(),
144        }
145    }
146}
147
148impl Default for LassoInterner {
149    fn default() -> Self {
150        Self::new()
151    }
152}
153
154impl StringInternerTrait for LassoInterner {
155    type Symbol = Spur;
156
157    /// Interns a string slice using the `lasso` interner.
158    ///
159    /// # Arguments
160    ///
161    /// * `s` - The string slice to intern.
162    ///
163    /// # Returns
164    ///
165    /// The `Spur` symbol representing the interned string.
166    fn intern(&mut self, s: &str) -> Self::Symbol {
167        self.interner.get_or_intern(s)
168    }
169
170    /// Resolves a `Spur` symbol back to its original string using the `lasso` interner.
171    ///
172    /// # Arguments
173    ///
174    /// * `symbol` - The `Spur` symbol to resolve.
175    ///
176    /// # Returns
177    ///
178    /// An owned `String` corresponding to the resolved symbol.
179    fn resolve(&self, symbol: &Self::Symbol) -> String {
180        self.interner.resolve(symbol).to_string()
181    }
182}
183
184/// An implementation of `StringInternerTrait` using the `interned-string` crate's `IString`.
185pub struct InternedStringInterner;
186
187impl InternedStringInterner {
188    /// Creates a new `InternedStringInterner` instance.
189    ///
190    /// # Returns
191    ///
192    /// A new `InternedStringInterner` instance.
193    pub fn new() -> Self {
194        Self
195    }
196}
197
198impl Default for InternedStringInterner {
199    fn default() -> Self {
200        Self::new()
201    }
202}
203
204impl StringInternerTrait for InternedStringInterner {
205    type Symbol = IString;
206
207    /// Interns a string slice using `IString::intern()`.
208    ///
209    /// # Arguments
210    ///
211    /// * `s` - The string slice to intern.
212    ///
213    /// # Returns
214    ///
215    /// The `IString` representing the interned string.
216    fn intern(&mut self, s: &str) -> Self::Symbol {
217        s.intern()
218    }
219
220    /// Resolves an `IString` back to its original string.
221    ///
222    /// # Arguments
223    ///
224    /// * `symbol` - The `IString` to resolve.
225    ///
226    /// # Returns
227    ///
228    /// An owned `String` corresponding to the resolved symbol.
229    fn resolve(&self, symbol: &Self::Symbol) -> String {
230        symbol.as_ref().to_string()
231    }
232}
233
234/// An implementation of `StringInternerTrait` using the `intern-string` crate.
235pub struct InternStringImplInterner {
236    interner: InternStringIntern<'static>,
237}
238
239impl InternStringImplInterner {
240    /// Creates a new `InternStringImplInterner` instance.
241    ///
242    /// # Returns
243    ///
244    /// A new `InternStringImplInterner` instance.
245    pub fn new() -> Self {
246        Self {
247            interner: InternStringIntern::new(),
248        }
249    }
250}
251
252impl Default for InternStringImplInterner {
253    fn default() -> Self {
254        Self::new()
255    }
256}
257
258impl StringInternerTrait for InternStringImplInterner {
259    type Symbol = InternStringInternId;
260
261    /// Interns a string slice using the `intern-string` interner.
262    ///
263    /// # Arguments
264    ///
265    /// * `s` - The string slice to intern.
266    ///
267    /// # Returns
268    ///
269    /// The `InternStringInternId` representing the interned string.
270    fn intern(&mut self, s: &str) -> Self::Symbol {
271        self.interner.intern(s)
272    }
273
274    /// Resolves an `InternStringInternId` back to its original string.
275    ///
276    /// # Arguments
277    ///
278    /// * `symbol` - The `InternStringInternId` to resolve.
279    ///
280    /// # Returns
281    ///
282    /// An owned `String` corresponding to the resolved symbol.
283    fn resolve(&self, symbol: &Self::Symbol) -> String {
284        self.interner.lookup(*symbol).to_string()
285    }
286}
287
288/// An implementation of `StringInternerTrait` using the `arc-string-interner` crate.
289pub struct ArcStringInternerImplInterner {
290    interner: ArcStringInternerImpl<ArcSym, std::collections::hash_map::RandomState, 10>,
291}
292
293impl ArcStringInternerImplInterner {
294    /// Creates a new `ArcStringInternerImplInterner` instance.
295    ///
296    /// # Returns
297    ///
298    /// A new `ArcStringInternerImplInterner` instance.
299    pub fn new() -> Self {
300        Self {
301            interner: ArcStringInternerImpl::with_capacity(1024), 
302        }
303    }
304}
305
306impl Default for ArcStringInternerImplInterner {
307    fn default() -> Self {
308        Self::new()
309    }
310}
311
312impl StringInternerTrait for ArcStringInternerImplInterner {
313    type Symbol = ArcSym; 
314
315    /// Interns a string slice using the `arc-string-interner`.
316    ///
317    /// # Arguments
318    ///
319    /// * `s` - The string slice to intern.
320    ///
321    /// # Returns
322    ///
323    /// The `ArcSym` representing the interned string.
324    fn intern(&mut self, s: &str) -> Self::Symbol {
325        self.interner.get_or_intern(s.to_string())
326    }
327
328    /// Resolves an `ArcSym` back to its original string.
329    ///
330    /// # Arguments
331    ///
332    /// * `symbol` - The `ArcSym` to resolve.
333    ///
334    /// # Returns
335    ///
336    /// An owned `String` corresponding to the resolved symbol.
337    fn resolve(&self, symbol: &Self::Symbol) -> String {
338        let arc_str_val: Arc<str> = self
339            .interner
340            .resolve(*symbol)
341            .expect("Symbol should exist in interner");
342        arc_str_val.to_string()
343    }
344}
345
346// Add a default impl for SharedStringInterner
347impl Default for SharedStringInterner {
348    fn default() -> Self {
349        Self::new()
350    }
351}
352
353/// An implementation of `StringInternerTrait` that does no actual interning.
354///
355/// This struct serves as a baseline for benchmarking, as it simply stores and
356/// returns owned `String`s without any interning optimization.
357#[derive(Default)]
358pub struct NoInterningBaseline {
359    // No shared state needed for this baseline, as each "interned" string
360    // is just an owned copy. If we needed to resolve symbols that are not
361    // &'a str themselves, we might need a Vec<String> here, but since
362    // Self::Symbol is String, resolve can just return a ref to the symbol.
363}
364
365impl NoInterningBaseline {
366    /// Creates a new `NoInterningBaseline` instance.
367    ///
368    /// # Returns
369    ///
370    /// A new `NoInterningBaseline` instance.
371    pub fn new() -> Self {
372        Self {}
373    }
374}
375
376impl StringInternerTrait for NoInterningBaseline {
377    /// The "symbol" type for this baseline is `String` itself, as no interning occurs.
378    type Symbol = String;
379
380    /// "Interns" a string slice by creating an owned copy.
381    ///
382    /// # Arguments
383    ///
384    /// * `s` - The string slice to "intern".
385    ///
386    /// # Returns
387    ///
388    /// An owned `String` copy of the input slice.
389    fn intern(&mut self, s: &str) -> Self::Symbol {
390        s.to_string()
391    }
392
393    /// Resolves a `String` symbol by simply cloning it.
394    ///
395    /// # Arguments
396    ///
397    /// * `symbol` - The `String` to resolve.
398    ///
399    /// # Returns
400    ///
401    /// A cloned `String` corresponding to the input symbol.
402    fn resolve(&self, symbol: &Self::Symbol) -> String {
403        symbol.clone()
404    }
405}
406
407// 1. StringBackendInterner (explicit, fresh instance)
408/// An implementation of `StringInternerTrait` using `string_interner::backend::StringBackend`.
409///
410/// This interner uses a `StringBackend` for storage, which is suitable for general-purpose
411/// string interning where strings are stored directly.
412pub struct StringBackendInterner {
413    interner: StringInterner<StringBackend, RandomState>,
414}
415
416impl StringBackendInterner {
417    /// Creates a new `StringBackendInterner` instance.
418    ///
419    /// # Returns
420    ///
421    /// A new `StringBackendInterner` instance.
422    pub fn new() -> Self {
423        Self {
424            interner: StringInterner::<StringBackend, RandomState>::new(),
425        }
426    }
427}
428
429impl Default for StringBackendInterner {
430    fn default() -> Self {
431        Self::new()
432    }
433}
434
435impl StringInternerTrait for StringBackendInterner {
436    type Symbol = DefaultSymbol;
437
438    /// Interns a string slice using the `StringBackend` interner.
439    ///
440    /// # Arguments
441    ///
442    /// * `s` - The string slice to intern.
443    ///
444    /// # Returns
445    ///
446    /// The `DefaultSymbol` representing the interned string.
447    fn intern(&mut self, s: &str) -> Self::Symbol {
448        self.interner.get_or_intern(s)
449    }
450
451    /// Resolves a `DefaultSymbol` back to its original string using the `StringBackend` interner.
452    ///
453    /// # Arguments
454    ///
455    /// * `symbol` - The `DefaultSymbol` to resolve.
456    ///
457    /// # Returns
458    ///
459    /// An owned `String` corresponding to the resolved symbol.
460    ///
461    /// # Panics
462    ///
463    /// Panics if the symbol cannot be resolved, which indicates an inconsistency.
464    fn resolve(&self, symbol: &Self::Symbol) -> String {
465        self.interner
466            .resolve(*symbol)
467            .expect("Symbol should exist in interner")
468            .to_owned()
469    }
470}
471
472/// An implementation of `StringInternerTrait` using `string_interner::backend::BucketBackend`.
473///
474/// This interner uses a `BucketBackend` for storage, which is optimized for scenarios
475/// where strings are grouped into buckets based on their hash, potentially reducing
476/// collision and improving lookup times for certain data distributions.
477pub struct BucketBackendInterner {
478    interner: StringInterner<BucketBackend, RandomState>,
479}
480
481impl BucketBackendInterner {
482    /// Creates a new `BucketBackendInterner` instance.
483    ///
484    /// # Returns
485    ///
486    /// A new `BucketBackendInterner` instance.
487    pub fn new() -> Self {
488        Self {
489            interner: StringInterner::<BucketBackend, RandomState>::new(),
490        }
491    }
492}
493
494impl Default for BucketBackendInterner {
495    fn default() -> Self {
496        Self::new()
497    }
498}
499
500impl StringInternerTrait for BucketBackendInterner {
501    type Symbol = DefaultSymbol;
502
503    /// Interns a string slice using the `BucketBackend` interner.
504    ///
505    /// # Arguments
506    ///
507    /// * `s` - The string slice to intern.
508    ///
509    /// # Returns
510    ///
511    /// The `DefaultSymbol` representing the interned string.
512    fn intern(&mut self, s: &str) -> Self::Symbol {
513        self.interner.get_or_intern(s)
514    }
515
516    /// Resolves a `DefaultSymbol` back to its original string using the `BucketBackend` interner.
517    ///
518    /// # Arguments
519    ///
520    /// * `symbol` - The `DefaultSymbol` to resolve.
521    ///
522    /// # Returns
523    ///
524    /// An owned `String` corresponding to the resolved symbol.
525    ///
526    /// # Panics
527    ///
528    /// Panics if the symbol cannot be resolved, which indicates an inconsistency.
529    fn resolve(&self, symbol: &Self::Symbol) -> String {
530        self.interner
531            .resolve(*symbol)
532            .expect("Symbol should exist in interner")
533            .to_owned()
534    }
535}
536
537/// An implementation of `StringInternerTrait` using `string_interner::backend::BufferBackend`.
538///
539/// This interner uses a `BufferBackend` for storage, which is designed for efficient
540/// storage of strings in a contiguous buffer, potentially offering better cache locality
541/// and performance for certain access patterns.
542pub struct BufferBackendInterner {
543    interner: StringInterner<BufferBackend, RandomState>,
544}
545
546impl BufferBackendInterner {
547    /// Creates a new `BufferBackendInterner` instance.
548    ///
549    /// # Returns
550    ///
551    /// A new `BufferBackendInterner` instance.
552    pub fn new() -> Self {
553        Self {
554            interner: StringInterner::<BufferBackend, RandomState>::new(),
555        }
556    }
557}
558
559impl Default for BufferBackendInterner {
560    fn default() -> Self {
561        Self::new()
562    }
563}
564
565impl StringInternerTrait for BufferBackendInterner {
566    type Symbol = DefaultSymbol;
567
568    /// Interns a string slice using the `BufferBackend` interner.
569    ///
570    /// # Arguments
571    ///
572    /// * `s` - The string slice to intern.
573    ///
574    /// # Returns
575    ///
576    /// The `DefaultSymbol` representing the interned string.
577    fn intern(&mut self, s: &str) -> Self::Symbol {
578        self.interner.get_or_intern(s)
579    }
580
581    /// Resolves a `DefaultSymbol` back to its original string using the `BufferBackend` interner.
582    ///
583    /// # Arguments
584    ///
585    /// * `symbol` - The `DefaultSymbol` to resolve.
586    ///
587    /// # Returns
588    ///
589    /// An owned `String` corresponding to the resolved symbol.
590    ///
591    /// # Panics
592    ///
593    /// Panics if the symbol cannot be resolved, which indicates an inconsistency.
594    fn resolve(&self, symbol: &Self::Symbol) -> String {
595        self.interner
596            .resolve(*symbol)
597            .expect("Symbol should exist in interner")
598            .to_owned()
599    }
600}