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}