• Stars
    star
    268
  • Rank 153,144 (Top 4 %)
  • Language
    Rust
  • License
    MIT License
  • Created about 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

a lock-free concurrent slab (experimental)

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.

More Repositories

1

mycelium

🍄 an alleged 'operating system'
Rust
532
star
2

tinymetrics

a minimal, allocation-free Prometheus/OpenMetrics metrics implementation for `no-std` and embedded Rust.
Rust
300
star
3

thingbuf

in-place allocation-reusing queues for Rust
Rust
282
star
4

seax

A VM-based runtime environment for functional programming languages
Rust
45
star
5

mnemosyne

A functional systems programming language with compile-time memory management
Rust
30
star
6

async-workshop

Rust
21
star
7

dotfiles

my dot files
Shell
19
star
8

decaf

like Java, but less so
Scala
17
star
9

tokio-trace-prototype

Rust
16
star
10

flake

eliza's entire computer: the git repo
Nix
16
star
11

eclss

Environmental Controls and Life Support Systems
Rust
14
star
12

matchers

Rust
11
star
13

seax_svm

Seax Virtual Machine
Rust
9
star
14

eliza_error

Rust
7
star
15

homebrew-x86_64-pc-elf

Homebrew formulae to install the x86_64-pc-elf cross compiler toolchain.
Ruby
7
star
16

multipass

yes, she knows it's a multipass. anyway, we're in love.
Rust
7
star
17

seax_scheme

Seax Scheme compiler
Rust
6
star
18

traverse

Doing science with filesystem traversal!
Python
6
star
19

tokio-trace

Rust
6
star
20

natatorium

"natatorium" is a fancy word for "swimming pool"
Rust
5
star
21

segvec

Rust
5
star
22

henrys_thing

a thing 4 henry
Rust
5
star
23

recipes

programs for making food!
4
star
24

async-cancel

fast, cheap, and out of control task cancelation
Rust
4
star
25

l-systems

L-Systems fun in Scala and Processing
Scala
3
star
26

hyper-compress

Compression middleware for Hyper
Rust
3
star
27

Orbit

Minimal SublimeText colorschemes intended for use with the Spacegray UI.
3
star
28

dtab.rs

a Rust library for parsing and constructing Finagle/Linkerd delegation tables
Rust
3
star
29

sqlviz

Quick SQL schema analysis & visualization in Python
Python
3
star
30

lin

Generic linear algebra in Rust
Rust
3
star
31

prologue

is_prolog(prologue) :- true
Rust
3
star
32

homebrew-grub

Homebrew tap for building GRUB
Ruby
3
star
33

grist

a simple Git web service in Rust
Rust
2
star
34

vupdaters

Streacom VU-Dials utilities
Rust
2
star
35

huffman.rs

A full-featured Huffman coding implementation in Rust
Rust
2
star
36

cargo-hell

hubris torment nexus
Rust
2
star
37

FitLine

FitBit analytics in PyLab
Python
2
star
38

tower-breaker

tower circuit-breaking experiments
Rust
2
star
39

advent-code

Haskell
2
star
40

seax_util

General purpose bits for compilers targeting the Seax platform
Rust
2
star
41

deebee

A tiny database
Scala
2
star
42

USL

USL is the Useless Stack Language - a toy stack-based language implemented by Hawk Weisman and Max Clive for Computer Science 420 at Allegheny College.
TeX
2
star
43

eclss-nostd

Environmental Control and Life Support Systems
Rust
2
star
44

floating-pointless

software floating-point arithmetic in Rust (for educational purposes only)
Rust
1
star
45

cordyceps

Rust intrusive collections library
1
star
46

snapmap

Rust
1
star
47

haskell-common

General-purpose Haskell code, mostly for practice
Haskell
1
star
48

rush

bad shell do not use
Rust
1
star
49

cs382-final

Final project for Computer Science 382: Visual Computing at Allegheny College.
Rust
1
star
50

CS111

CS111 junk
Java
1
star
51

weathergirl-rs

Rust
1
star
52

pyANOVA

Python command-line statistics tools, just for fun.
Python
1
star
53

unstable-macros

macros to reduce the boilerplate of conditionally supporting unstable Rust
Rust
1
star
54

cargo-loom

a Cargo command for running `loom` tests
Rust
1
star
55

old-dotfiles

my dot files
Python
1
star
56

tokio-fswatch

Rust
1
star
57

locality

Rust
1
star
58

CMPSC440

Computer Science 440 at Allegheny College
Java
1
star
59

JForthEngine

A simple Forth Virtual Machine in Java.
Java
1
star
60

hacktor

a secret thing
Rust
1
star
61

scala-common

General-purpose Scala code bits
Scala
1
star
62

DunGen

A quick 2D dungeon generator for roguelikes and such
Scala
1
star
63

ionesco

libraries for Scala and JavaScript interoperation
Scala
1
star
64

map_ext.rs

Extensions to Rust's std::collections::HashMap and std::collections::BTreeMap.
Rust
1
star
65

carrden

Carrden web app project
HTML
1
star
66

sen5x-rs

Rust
1
star
67

homebrew-cross

Ruby
1
star
68

routekicking

linkerd HTTPRoute policy tire-kicking
1
star
69

MIPSCodeGeneration

Experiments with code generation in C and MIPS. Currently targeting the MARSBot graphics turtle that ships with MARS4.4, may later add code generation for other applications.
Assembly
1
star
70

line-filter

a `tracing` filter for enabling individual events by line
Rust
1
star
71

weathergirl

a cute little arduino weather station
C++
1
star
72

breadplan

A simple Python script to generate baking plans for the recipes in Tartine Bread
Mathematica
1
star
73

senior-thesis

TeX
1
star
74

hawkw.github.io

CSS
1
star
75

thoughts

A place to collect notes and ideas.
1
star
76

scattergather

utilities to help make vectored IO easy
Rust
1
star
77

futures-log

A logging wrapper around Rust Futures and Streams, to help with debugging
Rust
1
star
78

lazy

Rust
1
star
79

Sieve

Contains an implementation of the Sieve of Eratosthenes in Java and C for CMPSC220 Lab 1
Java
1
star
80

tokio-buffer-repro

Rust
1
star