All Projects → hawkw → sharded-slab

hawkw / sharded-slab

Licence: MIT license
a lock-free concurrent slab (experimental)

Programming Languages

rust
11053 projects
shell
77523 projects

Projects that are alternatives of or similar to sharded-slab

Sled
the champagne of beta embedded databases
Stars: ✭ 5,423 (+4575%)
Mutual labels:  concurrent, lock-free
treap
A thread-safe, persistent Treap (tree + heap) for ordered key-value mapping and priority sorting.
Stars: ✭ 23 (-80.17%)
Mutual labels:  concurrent
java-multithread
Códigos feitos para o curso de Multithreading com Java, no canal RinaldoDev do YouTube.
Stars: ✭ 24 (-79.31%)
Mutual labels:  concurrent
go-soda
Socrata Open Data API (SODA) GET client for Golang
Stars: ✭ 40 (-65.52%)
Mutual labels:  concurrent
lock-free-queue
CN-CppUserGroup-2019-1,lock-free queue demo
Stars: ✭ 58 (-50%)
Mutual labels:  lock-free
sled
A high performance lock free map type for go.
Stars: ✭ 18 (-84.48%)
Mutual labels:  lock-free
lfqueue
lock-free FIFO queue by C native built it, easy built cross platform(no extra dependencies needed) , guarantee thread safety memory management ever!
Stars: ✭ 104 (-10.34%)
Mutual labels:  lock-free
mapreduce
A in-process MapReduce library to help you optimizing service response time or concurrent task processing.
Stars: ✭ 93 (-19.83%)
Mutual labels:  concurrent
web-scraping-engine
A simple web scraping engine supporting concurrent and anonymous scraping
Stars: ✭ 27 (-76.72%)
Mutual labels:  concurrent
goroutines
provides utilities to perform common tasks on goroutines
Stars: ✭ 19 (-83.62%)
Mutual labels:  concurrent
fifo-rs
A first-in-first-out for bytes, like kfifo in Linux.
Stars: ✭ 18 (-84.48%)
Mutual labels:  lock-free
talepy
📚Coordinate "transactions" across a number of services in python
Stars: ✭ 20 (-82.76%)
Mutual labels:  concurrent
jdk-source-code-reading
JDK source code reading
Stars: ✭ 19 (-83.62%)
Mutual labels:  concurrent
gdax-orderbook-hpp
An in-memory copy of the order book on the GDAX cryptocurrency exchange, updated in real time via WebSocket feed, exposed in a thread-safe and lock-free data structure.
Stars: ✭ 38 (-67.24%)
Mutual labels:  lock-free
uniq
A lock-free (multi reader / multi writer) circular buffered queue.
Stars: ✭ 32 (-72.41%)
Mutual labels:  lock-free
mango
Core utility library & data connectors designed for simpler usage in Scala
Stars: ✭ 41 (-64.66%)
Mutual labels:  concurrent
optimistic lock coupling rs
🍋: A General Lock following paper "Optimistic Lock Coupling: A Scalable and Efficient General-Purpose Synchronization Method"
Stars: ✭ 21 (-81.9%)
Mutual labels:  lock-free
lock-free
Lock-Free data structures
Stars: ✭ 37 (-68.1%)
Mutual labels:  lock-free
lfqueue
Minimize lock-free queue ever!
Stars: ✭ 107 (-7.76%)
Mutual labels:  lock-free
concurrent-data-structure
Concurrent Data Structure for Rust
Stars: ✭ 18 (-84.48%)
Mutual labels:  concurrent

sharded-slab

A lock-free concurrent slab.

Crates.io Documentation CI Status GitHub License maintenance status

Slabs provide pre-allocated storage for many instances of a single data type. When a large number of values of a single type are required, this can be more efficient than allocating each item individually. Since the allocated items are the same size, memory fragmentation is reduced, and creating and removing new items can be very cheap.

This crate implements a lock-free concurrent slab, indexed by usizes.

Note: This crate is currently experimental. Please feel free to use it in your projects, but bear in mind that there's still plenty of room for optimization, and there may still be some lurking bugs.

Usage

First, add this to your Cargo.toml:

sharded-slab = "0.1.1"

This crate provides two types, Slab and Pool, which provide slightly different APIs for using a sharded slab.

Slab implements a slab for storing small types, sharing them between threads, and accessing them by index. New entries are allocated by inserting data, moving it in by value. Similarly, entries may be deallocated by taking from the slab, moving the value out. This API is similar to a Vec<Option<T>>, but allowing lock-free concurrent insertion and removal.

In contrast, the Pool type provides an object pool style API for reusing storage. Rather than constructing values and moving them into the pool, as with Slab, allocating an entry from the pool takes a closure that's provided with a mutable reference to initialize the entry in place. When entries are deallocated, they are cleared in place. Types which own a heap allocation can be cleared by dropping any data they store, but retaining any previously-allocated capacity. This means that a Pool may be used to reuse a set of existing heap allocations, reducing allocator load.

Examples

Inserting an item into the slab, returning an index:

use sharded_slab::Slab;
let slab = Slab::new();

let key = slab.insert("hello world").unwrap();
assert_eq!(slab.get(key).unwrap(), "hello world");

To share a slab across threads, it may be wrapped in an Arc:

use sharded_slab::Slab;
use std::sync::Arc;
let slab = Arc::new(Slab::new());

let slab2 = slab.clone();
let thread2 = std::thread::spawn(move || {
    let key = slab2.insert("hello from thread two").unwrap();
    assert_eq!(slab2.get(key).unwrap(), "hello from thread two");
    key
});

let key1 = slab.insert("hello from thread one").unwrap();
assert_eq!(slab.get(key1).unwrap(), "hello from thread one");

// Wait for thread 2 to complete.
let key2 = thread2.join().unwrap();

// The item inserted by thread 2 remains in the slab.
assert_eq!(slab.get(key2).unwrap(), "hello from thread two");

If items in the slab must be mutated, a Mutex or RwLock may be used for each item, providing granular locking of items rather than of the slab:

use sharded_slab::Slab;
use std::sync::{Arc, Mutex};
let slab = Arc::new(Slab::new());

let key = slab.insert(Mutex::new(String::from("hello world"))).unwrap();

let slab2 = slab.clone();
let thread2 = std::thread::spawn(move || {
    let hello = slab2.get(key).expect("item missing");
    let mut hello = hello.lock().expect("mutex poisoned");
    *hello = String::from("hello everyone!");
});

thread2.join().unwrap();

let hello = slab.get(key).expect("item missing");
let mut hello = hello.lock().expect("mutex poisoned");
assert_eq!(hello.as_str(), "hello everyone!");

Comparison with Similar Crates

  • slab: Carl Lerche's slab crate provides a slab implementation with a similar API, implemented by storing all data in a single vector.

    Unlike sharded-slab, inserting and removing elements from the slab requires mutable access. This means that if the slab is accessed concurrently by multiple threads, it is necessary for it to be protected by a Mutex or RwLock. Items may not be inserted or removed (or accessed, if a Mutex is used) concurrently, even when they are unrelated. In many cases, the lock can become a significant bottleneck. On the other hand, sharded-slab allows separate indices in the slab to be accessed, inserted, and removed concurrently without requiring a global lock. Therefore, when the slab is shared across multiple threads, this crate offers significantly better performance than slab.

    However, the lock free slab introduces some additional constant-factor overhead. This means that in use-cases where a slab is not shared by multiple threads and locking is not required, sharded-slab will likely offer slightly worse performance.

    In summary: sharded-slab offers significantly improved performance in concurrent use-cases, while slab should be preferred in single-threaded use-cases.

Safety and Correctness

Most implementations of lock-free data structures in Rust require some amount of unsafe code, and this crate is not an exception. In order to catch potential bugs in this unsafe code, we make use of loom, a permutation-testing tool for concurrent Rust programs. All unsafe blocks this crate occur in accesses to loom UnsafeCells. This means that when those accesses occur in this crate's tests, loom will assert that they are valid under the C11 memory model across multiple permutations of concurrent executions of those tests.

In order to guard against the ABA problem, this crate makes use of generational indices. Each slot in the slab tracks a generation counter which is incremented every time a value is inserted into that slot, and the indices returned by Slab::insert include the generation of the slot when the value was inserted, packed into the high-order bits of the index. This ensures that if a value is inserted, removed, and a new value is inserted into the same slot in the slab, the key returned by the first call to insert will not map to the new value.

Since a fixed number of bits are set aside to use for storing the generation counter, the counter will wrap around after being incremented a number of times. To avoid situations where a returned index lives long enough to see the generation counter wrap around to the same value, it is good to be fairly generous when configuring the allocation of index bits.

Performance

These graphs were produced by benchmarks of the sharded slab implementation, using the criterion crate.

The first shows the results of a benchmark where an increasing number of items are inserted and then removed into a slab concurrently by five threads. It compares the performance of the sharded slab implementation with a RwLock<slab::Slab>:

Screen Shot 2019-10-01 at 5 09 49 PM

The second graph shows the results of a benchmark where an increasing number of items are inserted and then removed by a single thread. It compares the performance of the sharded slab implementation with an RwLock<slab::Slab> and a mut slab::Slab.

Screen Shot 2019-10-01 at 5 13 45 PM

These benchmarks demonstrate that, while the sharded approach introduces a small constant-factor overhead, it offers significantly better performance across concurrent accesses.

License

This project is licensed under the MIT license.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, shall be licensed as MIT, without any additional terms or conditions.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].