3 releases
0.1.3 | Nov 30, 2022 |
---|---|
0.1.2 | Nov 29, 2022 |
0.1.1 | Nov 28, 2022 |
#969 in Concurrency
37KB
671 lines
syncmap
syncmap
syncmap is a fast, concurrent cache library
The motivation to build syncmap comes from the sync.Map in Golang.
Summary
Map is like a Hashmap but is safe for concurrent use by multiple thread without additional locking or coordination. Loads, stores, and deletes run in amortized constant time.
The Map type is specialized. Most code should use a plain Rust HashMap instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.
The Map type is optimized for two common use cases: (1) when the entry for a given key is only ever written once but read many times, as in caches that only grow, or (2) when multiple thread read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a Map may significantly reduce lock contention compared to a Rust HashMap paired with a separate Mutex or RWMutex.
The zero Map is empty and ready for use. A Map must not be copied after first use.
In the terminology of the Go memory model, Map arranges that a write operation “synchronizes before” any read operation that observes the effect of the write, where read and write operations are defined as follows. Load, LoadAndDelete, LoadOrStore are read operations; Delete, LoadAndDelete, and Store are write operations; and LoadOrStore is a write operation when it returns loaded set to false.
Status
syncmap is usable but still under active development. We expect it to be production ready in the near future.
Usage
Example
// concurrent exmaple
use syncmap::map::Map;
fn main() {
let map = Arc::new(Map::<usize, usize>::new());
let map1 = map.clone();
let t1 = std::thread::spawn(move || {
for i in 0..5000 {
map1.insert(i, 0, &map1.guard());
}
});
let map2 = map.clone();
let t2 = std::thread::spawn(move || {
for i in 0..5000 {
map2.insert(i, 1, &map2.guard());
}
});
t1.join().unwrap();
t2.join().unwrap();
thread::sleep(Duration::from_micros(1000));
let mut missed = 0;
let guard = map.guard();
for i in 0..5000 {
let v = map.get(&i, &guard);
if v.is_some() {
assert!(v == Some(&0) || v == Some(&1));
} else {
missed += 1;
}
// let kv = map.get_key_value(&i, &guard).unwrap();
// assert!(kv == (&i, &0) || kv == (&i, &1));
}
println!("missed {}", missed)
}
use syncmap::map::Map;
fn main() {
let cache = Map::new();
let guard = cache.guard();
cache.insert("key", "value1", &guard);
thread::sleep(Duration::from_millis(50));
assert_eq!(cache.get(&"key", &guard), Some("value1"));
}
Dependencies
~5–11MB
~105K SLoC