[][src]Crate lasso

CI Security Audit Coverage LoC Docs.rs Crates.io

A multithreaded and single threaded string interner that allows strings to be cached with a minimal memory footprint, associating them with a unique key that can be used to retrieve them at any time. A Rodeo allows O(1) internment and resolution and can be turned into a RodeoReader to allow for contention-free resolutions with both key to str and str to key operations. It can also be turned into a RodeoResolver with only key to str operations for the lowest possible memory usage.

Which interner do I use?

For single-threaded workloads Rodeo is encouraged, while multi-threaded applications should use ThreadedRodeo. Both of these are the only way to intern strings, but most applications will hit a stage where they are done interning strings, and at that point is where the choice between RodeoReader and RodeoResolver. If the user needs to get keys for strings still, then they must use the RodeoReader (although they can still transfer into a RodeoResolver) at this point. For users who just need key to string resolution, the RodeoResolver gives contention-free access at the minimum possible memory usage. Note that to gain access to ThreadedRodeo the multi-threaded feature is required.

InternerThread-safeIntern Stringstr to keykey to strContention FreeMemory Usage
RodeoN/AMedium
ThreadedRodeoMost
RodeoReaderMedium
RodeoResolverLeast

Cargo Features

By default lasso has zero dependencies and only Rodeo is exposed. To make use of ThreadedRodeo, you must enable the multi-threaded feature.

  • multi-threaded - Enables ThreadedRodeo, the interner for multi-threaded tasks
  • hashbrown-table - Uses hashbrown as the internal HashMap
  • ahasher - Use ahash's RandomState as the default hasher
  • no-std - Enables no_std + alloc support for Rodeo and ThreadedRodeo
    • Automatically enables the following required features:
      • dashmap/no_std - no_std compatibility for DashMap
      • hashbrown-table - no_std HashMap
      • ahasher - no_std hashing function
  • serialize - Implements Serialize and Deserialize for all Spur types

Example: Using Rodeo

use lasso::Rodeo;

let mut rodeo = Rodeo::default();
let key = rodeo.get_or_intern("Hello, world!");

// Easily retrieve the value of a key and find the key for values
assert_eq!("Hello, world!", rodeo.resolve(&key));
assert_eq!(Some(key), rodeo.get("Hello, world!"));

// Interning the same string again will yield the same key
let key2 = rodeo.get_or_intern("Hello, world!");

assert_eq!(key, key2);

Example: Using ThreadedRodeo

use lasso::ThreadedRodeo;
use std::{thread, sync::Arc};

let rodeo = Arc::new(ThreadedRodeo::default());
let key = rodeo.get_or_intern("Hello, world!");

// Easily retrieve the value of a key and find the key for values
assert_eq!("Hello, world!", rodeo.resolve(&key));
assert_eq!(Some(key), rodeo.get("Hello, world!"));

// Interning the same string again will yield the same key
let key2 = rodeo.get_or_intern("Hello, world!");

assert_eq!(key, key2);

// ThreadedRodeo can be shared across threads
let moved = Arc::clone(&rodeo);
let hello = thread::spawn(move || {
    assert_eq!("Hello, world!", moved.resolve(&key));
    moved.get_or_intern("Hello from the thread!")
})
.join()
.unwrap();

assert_eq!("Hello, world!", rodeo.resolve(&key));
assert_eq!("Hello from the thread!", rodeo.resolve(&hello));

Example: Creating a RodeoReader

use lasso::Rodeo;

// Rodeo and ThreadedRodeo are interchangeable here
let mut rodeo = Rodeo::default();

let key = rodeo.get_or_intern("Hello, world!");
assert_eq!("Hello, world!", rodeo.resolve(&key));

let reader = rodeo.into_reader();

// Reader keeps all the strings from the parent
assert_eq!("Hello, world!", reader.resolve(&key));
assert_eq!(Some(key), reader.get("Hello, world!"));

// The Reader can now be shared across threads, no matter what kind of Rodeo created it

Example: Creating a RodeoResolver

use lasso::Rodeo;

// Rodeo and ThreadedRodeo are interchangeable here
let mut rodeo = Rodeo::default();

let key = rodeo.get_or_intern("Hello, world!");
assert_eq!("Hello, world!", rodeo.resolve(&key));

let resolver = rodeo.into_resolver();

// Resolver keeps all the strings from the parent
assert_eq!("Hello, world!", resolver.resolve(&key));

// The Resolver can now be shared across threads, no matter what kind of Rodeo created it

Benchmarks

Benchmarks were gathered with Criterion.rs
OS: Windows 10
CPU: Ryzen 9 3900X at 3800Mhz
RAM: 3200Mhz
Rustc: Stable 1.43.1

Rodeo

Std's RandomState

MethodTimeThroughput
resolve1.9356 μs13.214 GiB/s
try_resolve1.9389 μs13.191 GiB/s
resolve_unchecked1.4643 μs17.467 GiB/s
get_or_intern (empty)95.214 μs275.06 MiB/s
get_or_intern (filled)57.163 μs458.16 MiB/s
try_get_or_intern (empty)93.504 μs280.09 MiB/s
try_get_or_intern (filled)57.030 μs459.23 MiB/s
get (empty)36.120 μs725.08 MiB/s
get (filled)50.915 μs514.38 MiB/s

AHash's RandomState

MethodTimeThroughput
resolve1.9338 μs13.226 GiB/s
try_resolve1.9468 μs13.137 GiB/s
resolve_unchecked1.4503 μs17.635 GiB/s
get_or_intern (empty)56.413 μs464.25 MiB/s
get_or_intern (filled)29.770 μs879.73 MiB/s
try_get_or_intern (empty)59.106 μs443.10 MiB/s
try_get_or_intern (filled)31.195 μs839.54 MiB/s
get (empty)9.8542 μs2.5954 GiB/s
get (filled)23.113 μs1.1065 GiB/s

FxHash's FxBuildHasher

MethodTimeThroughput
resolve2.0569 μs12.434 GiB/s
try_resolve1.9505 μs13.113 GiB/s
resolve_unchecked1.4477 μs17.666 GiB/s
get_or_intern (empty)44.392 μs589.97 MiB/s
get_or_intern (filled)27.645 μs947.36 MiB/s
try_get_or_intern (empty)43.947 μs595.95 MiB/s
try_get_or_intern (filled)27.085 μs966.95 MiB/s
get (empty)9.4772 μs2.6987 GiB/s
get (filled)27.332 μs958.20 MiB/s

ThreadedRodeo

Std's RandomState

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve55.715 μs470.07 MiB/s354.01 μs73.981 MiB/s
try_resolve55.117 μs475.17 MiB/s380.16 μs68.892 MiB/s
get_or_intern (empty)282.62 μs92.666 MiB/sN\AN\A
get_or_intern (filled)103.41 μs253.26 MiB/s433.80 μs60.373 MiB/s
try_get_or_intern (empty)287.55 μs91.079 MiB/sN\AN\A
try_get_or_intern (filled)105.35 μs248.59 MiB/s447.55 μs58.518 MiB/s
get (empty)86.328 μs303.37 MiB/sN\AN\A
get (filled)95.673 μs273.74 MiB/s465.93 μs56.210 MiB/s

AHash's RandomState

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve20.103 μs1.2722 GiB/s258.78 μs101.20 MiB/s
try_resolve17.328 μs1.4760 GiB/s239.13 μs109.52 MiB/s
get_or_intern (empty)161.98 μs161.68 MiB/sN\AN\A
get_or_intern (filled)50.065 μs523.11 MiB/s346.60 μs75.563 MiB/s
try_get_or_intern (empty)159.84 μs163.85 MiB/sN\AN\A
try_get_or_intern (filled)51.366 μs509.86 MiB/s331.92 μs78.904 MiB/s
get (empty)36.637 μs714.84 MiB/sN\AN\A
get (filled)44.606 μs587.13 MiB/s341.70 μs76.645 MiB/s

FxHash's FxBuildHasher

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve20.475 μs1.2491 GiB/s230.52 μs113.61 MiB/s
try_resolve17.479 μs1.4632 GiB/s231.18 μs113.29 MiB/s
get_or_intern (empty)153.62 μs170.48 MiB/sN\AN\A
get_or_intern (filled)44.232 μs592.10 MiB/s297.39 μs88.065 MiB/s
try_get_or_intern (empty)151.58 μs172.78 MiB/sN\AN\A
try_get_or_intern (filled)45.125 μs580.39 MiB/s298.54 μs87.726 MiB/s
get (empty)33.043 μs792.61 MiB/sN\AN\A
get (filled)39.044 μs670.78 MiB/s297.38 μs88.068 MiB/s

RodeoReader

Std's RandomState

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve1.9425 μs13.167 GiB/s4.4657 μs5.7272 GiB/s
resolve_unchecked1.4826 μs17.251 GiB/s3.1239 μs8.1872 GiB/s
try_resolve1.9535 μs13.092 GiB/s4.1641 μs6.1420 GiB/s
get (empty)35.895 μs729.62 MiB/s97.991 μs267.27 MiB/s
get (filled)51.805 μs505.54 MiB/sN\AN\A

AHash's RandomState

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve1.9478 μs13.131 GiB/s4.1532 μs6.1582 GiB/s
resolve_unchecked1.4713 μs17.384 GiB/s3.0922 μs8.2710 GiB/s
try_resolve1.9584 μs13.059 GiB/s4.2616 μs6.0015 GiB/s
get (empty)9.9847 μs2.5615 GiB/s48.875 μs535.86 MiB/s
get (filled)22.848 μs1.1194 GiB/sN\AN\A

FxHash's FxBuildHasher

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve1.9588 μs13.057 GiB/s4.2030 μs6.0852 GiB/s
resolve_unchecked1.4866 μs17.204 GiB/s3.2421 μs7.8886 GiB/s
try_resolve1.9464 μs13.140 GiB/s4.2429 μs6.0279 GiB/s
get (empty)9.5245 μs2.6853 GiB/s48.011 μs545.49 MiB/s
get (filled)27.486 μs952.84 MiB/sN\AN\A

RodeoResolver

MethodTime (1 Thread)Throughput (1 Thread)Time (24 Threads)Throughput (24 Threads)
resolve1.9561 μs13.075 GiB/s4.1818 μs6.1160 GiB/s
resolve_unchecked1.7038 μs15.011 GiB/s3.1031 μs8.2420 GiB/s
try_resolve1.9490 μs13.123 GiB/s4.3075 μs5.9376 GiB/s

Other Interners (with std's RandomState)

string-internerTimeThroughputRelative Perf vs Rodeo
resolve3.8132 μs6.7072 GiB/s-49.23%
resolve_unchecked2.3976 μs10.667 GiB/s-38.92%
get_or_intern (empty)288.12 μs90.899 MiB/s-66.95%
get_or_intern (filled)60.104 μs435.74 MiB/s-5.114%
get (empty)40.496 μs646.72 MiB/s-10.80%
get (filled)63.797 μs410.52 MiB/s-20.19%

Nightly Benches

When the nightly feature is enabled, this is the performance you can expect from Rodeo.
The functions listed are the ones currently affected by the changes of the nightly feature, and the benchmarks were preformed with std's RandomState.
Testing was done on Rust Nightly 1.45.0

MethodTimeThroughputRelative Perf vs Stable
get_or_intern (empty)94.516 μs277.09 MiB/s+0.73%
get_or_intern (filled)56.716 μs461.77 MiB/s+0.78%
try_get_or_intern (empty)94.629 μs276.76 MiB/s-1.188%
try_get_or_intern (filled)56.839 μs460.77 MiB/s+0.336%

Structs

LargeSpur

The default key for every Rodeo, the same size as a usize

MicroSpur

A miniature Key utilizing only 8 bits of space

MiniSpur

A miniature Key utilizing only 16 bits of space

Rodeo

A string interner that caches strings quickly with a minimal memory footprint, returning a unique key to re-access it with O(1) internment and resolution.

RodeoReader

A read-only view of a Rodeo or ThreadedRodeo that allows contention-free access to interned strings, both key to string resolution and string to key lookups

RodeoResolver

A read-only view of a Rodeo or ThreadedRodeo that allows contention-free access to interned strings with only key to string resolution

Spur

The default key for every Rodeo, uses only 32bits of space

ThreadedRodeo

A concurrent string interner that caches strings quickly with a minimal memory footprint, returning a unique key to re-access it with O(1) internment and resolution.

Traits

Key

Types implementing this trait can be used as keys for all Rodeos