Skip to content

Commit 81d84ee

Browse files
authored
Merge pull request #73 from jonhoo/left-right
This patch set sxtract the core concurrency primitive from evmap by implementing the plan from #45 (comment). It fixes #45 and #70 by allowing separate implementations of the data structure that re-use the concurrency primitive. Fixes #67 by making `ReadGuard::map` public.
2 parents d4c12b8 + b334b2c commit 81d84ee

35 files changed

+2349
-1679
lines changed

Cargo.toml

+2-36
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,2 @@
1-
[package]
2-
name = "evmap"
3-
version = "11.0.0-alpha.1"
4-
authors = ["Jon Gjengset <[email protected]>"]
5-
edition = "2018"
6-
license = "MIT OR Apache-2.0"
7-
8-
readme = "README.md"
9-
description = "A lock-free, eventually consistent, concurrent multi-value map."
10-
repository = "https://github.com/jonhoo/rust-evmap.git"
11-
12-
keywords = ["map","multi-value","lock-free"]
13-
categories = ["concurrency", "data-structures"]
14-
15-
[badges]
16-
azure-devops = { project = "jonhoo/jonhoo", pipeline = "evmap", build = "8" }
17-
codecov = { repository = "jonhoo/rust-evmap", branch = "master", service = "github" }
18-
maintenance = { status = "passively-maintained" }
19-
20-
[features]
21-
default = []
22-
indexed = ["indexmap"]
23-
eviction = ["indexed", "rand"]
24-
25-
[dependencies]
26-
indexmap = { version = "1.1.0", optional = true }
27-
smallvec = "1.0.0"
28-
hashbag = "0.1.2"
29-
bytes = { version = "0.5", optional = true }
30-
rand = { version = "0.7", default-features = false, features = ["alloc"], optional = true }
31-
slab = "0.4"
32-
33-
[dev-dependencies]
34-
quickcheck = "0.9"
35-
quickcheck_macros = "0.9"
36-
rand = "0.7"
1+
[workspace]
2+
members = ["evmap", "evmap-derive", "left-right"]

README.md

+19-30
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,30 @@
1-
# evmap
1+
This crate holds the implementation of the `left-right` concurrency
2+
primitive, and its primary user `evmap`. See the documentation for each
3+
crate for details.
4+
5+
Left-right is a concurrency primitive for high concurrency reads over a
6+
single-writer data structure. The primitive keeps two copies of the
7+
backing data structure, one that is accessed by readers, and one that is
8+
access by the (single) writer. This enables all reads to proceed in
9+
parallel with minimal coordination, and shifts the coordination overhead
10+
to the writer. In the absence of writes, reads scale linearly with the
11+
number of cores.
12+
13+
[![Crates.io](https://img.shields.io/crates/v/left-right.svg)](https://crates.io/crates/left-right)
14+
[![Documentation](https://docs.rs/left-right/badge.svg)](https://docs.rs/left-right/)
215

316
[![Crates.io](https://img.shields.io/crates/v/evmap.svg)](https://crates.io/crates/evmap)
417
[![Documentation](https://docs.rs/evmap/badge.svg)](https://docs.rs/evmap/)
18+
519
[![Build Status](https://dev.azure.com/jonhoo/jonhoo/_apis/build/status/evmap?branchName=master)](https://dev.azure.com/jonhoo/jonhoo/_build/latest?definitionId=8&branchName=master)
620
[![Codecov](https://codecov.io/github/jonhoo/rust-evmap/coverage.svg?branch=master)](https://codecov.io/gh/jonhoo/rust-evmap)
721

8-
A lock-free, eventually consistent, concurrent multi-value map.
9-
10-
This map implementation allows reads and writes to execute entirely in parallel, with no
11-
implicit synchronization overhead. Reads never take locks on their critical path, and neither
12-
do writes assuming there is a single writer (multi-writer is possible using a `Mutex`), which
13-
significantly improves performance under contention.
14-
15-
The trade-off exposed by this module is one of eventual consistency: writes are not visible to
16-
readers except following explicit synchronization. Specifically, readers only see the
17-
operations that preceeded the last call to `WriteHandle::refresh` by a writer. This lets
18-
writers decide how stale they are willing to let reads get. They can refresh the map after
19-
every write to emulate a regular concurrent `HashMap`, or they can refresh only occasionally to
20-
reduce the synchronization overhead at the cost of stale reads.
21-
22-
For read-heavy workloads, the scheme used by this module is particularly useful. Writers can
23-
afford to refresh after every write, which provides up-to-date reads, and readers remain fast
24-
as they do not need to ever take locks.
25-
26-
The map is multi-value, meaning that every key maps to a *collection* of values. This
27-
introduces some memory cost by adding a layer of indirection through a `Vec` for each value,
28-
but enables more advanced use. This choice was made as it would not be possible to emulate such
29-
functionality on top of the semantics of this map (think about it -- what would the operational
30-
log contain?).
31-
32-
To faciliate more advanced use-cases, each of the two maps also carry some customizeable
33-
meta-information. The writers may update this at will, and when a refresh happens, the current
34-
meta will also be made visible to readers. This could be useful, for example, to indicate what
35-
time the refresh happened.
36-
3722
## Performance
3823

24+
**These benchmarks are outdated at this point, but communicate the right
25+
point. Hopefully I'll have a chance to update them again some time
26+
soon.**
27+
3928
I've run some benchmarks of evmap against a standard Rust `HashMap` protected
4029
by a [reader-writer
4130
lock](https://doc.rust-lang.org/std/sync/struct.RwLock.html), as well as

azure-pipelines.yml

+90-72
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,93 @@
1-
jobs:
2-
- template: default.yml@templates
3-
parameters:
4-
minrust: 1.40.0
5-
codecov_token: $(CODECOV_TOKEN_SECRET)
6-
- job: derive
7-
displayName: "Test evmap-derive"
8-
pool:
9-
vmImage: ubuntu-latest
10-
steps:
11-
- template: install-rust.yml@templates
12-
- script: cargo test
13-
displayName: cargo test
14-
- job: benchmark
15-
displayName: "Check that benchmark compiles"
16-
pool:
17-
vmImage: ubuntu-latest
18-
steps:
19-
- template: install-rust.yml@templates
20-
- bash: cd benchmark && cargo check
21-
displayName: cargo check benchmark/
22-
- job: miri
23-
displayName: "Run miri on test suite"
24-
pool:
25-
vmImage: ubuntu-latest
26-
steps:
27-
- bash: |
28-
echo '##vso[task.setvariable variable=nightly]nightly-'$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
29-
displayName: "Determine latest miri nightly"
30-
- template: install-rust.yml@templates
31-
parameters:
32-
rust: $(nightly)
33-
components:
34-
- miri
35-
- script: cargo miri setup
36-
displayName: cargo miri setup
37-
- script: cargo miri test
38-
displayName: cargo miri test
39-
env:
40-
QUICKCHECK_TESTS: 500
41-
- job: asan
42-
displayName: "Run address sanitizer on test suite"
43-
pool:
44-
vmImage: ubuntu-latest
45-
steps:
46-
- template: install-rust.yml@templates
47-
parameters:
48-
rust: nightly
49-
- bash: |
50-
sudo ln -s /usr/bin/llvm-symbolizer-6.0 /usr/bin/llvm-symbolizer
51-
displayName: Enable debug symbols
52-
# only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945
53-
- script: |
54-
env ASAN_OPTIONS="detect_odr_violation=0" RUSTFLAGS="-Z sanitizer=address" cargo test --lib --tests --target x86_64-unknown-linux-gnu
55-
displayName: cargo -Z sanitizer=address test
56-
- job: lsan
57-
displayName: "Run leak sanitizer on test suite"
58-
pool:
59-
vmImage: ubuntu-latest
60-
steps:
61-
- template: install-rust.yml@templates
62-
parameters:
63-
rust: nightly
64-
- bash: |
65-
sudo ln -s /usr/bin/llvm-symbolizer-6.0 /usr/bin/llvm-symbolizer
66-
sed -i '/\[features\]/i [profile.dev]' Cargo.toml
67-
sed -i '/profile.dev/a opt-level = 1' Cargo.toml
68-
cat Cargo.toml
69-
displayName: Enable debug symbols
70-
- script: |
71-
env RUSTFLAGS="-Z sanitizer=leak" cargo test --target x86_64-unknown-linux-gnu
72-
displayName: cargo -Z sanitizer=leak test
1+
stages:
2+
- stage: left_right
3+
jobs:
4+
- template: default.yml@templates
5+
parameters:
6+
minrust: 1.40.0
7+
dir: "left-right"
8+
- stage: evmap
9+
jobs:
10+
- template: default.yml@templates
11+
parameters:
12+
minrust: 1.40.0
13+
codecov_token: $(CODECOV_TOKEN_SECRET)
14+
dir: "evmap"
15+
- job: derive
16+
displayName: "Test evmap-derive"
17+
pool:
18+
vmImage: ubuntu-latest
19+
steps:
20+
- template: install-rust.yml@templates
21+
parameters:
22+
components:
23+
- rust-src
24+
- script: cargo test
25+
displayName: cargo test
26+
workingDirectory: "evmap-derive"
27+
- job: benchmark
28+
displayName: "Check that benchmark compiles"
29+
pool:
30+
vmImage: ubuntu-latest
31+
steps:
32+
- template: install-rust.yml@templates
33+
- script: cargo check
34+
displayName: cargo check benchmark/
35+
workingDirectory: "evmap/benchmark"
36+
- job: miri
37+
displayName: "Run miri on test suite"
38+
pool:
39+
vmImage: ubuntu-latest
40+
steps:
41+
- bash: |
42+
echo '##vso[task.setvariable variable=nightly]nightly-'$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
43+
displayName: "Determine latest miri nightly"
44+
- template: install-rust.yml@templates
45+
parameters:
46+
rust: $(nightly)
47+
components:
48+
- miri
49+
- script: cargo miri setup
50+
displayName: cargo miri setup
51+
workingDirectory: "evmap"
52+
- script: cargo miri test
53+
displayName: cargo miri test
54+
workingDirectory: "evmap"
55+
env:
56+
QUICKCHECK_TESTS: 500
57+
- job: asan
58+
displayName: "Run address sanitizer on test suite"
59+
pool:
60+
vmImage: ubuntu-latest
61+
steps:
62+
- template: install-rust.yml@templates
63+
parameters:
64+
rust: nightly
65+
- bash: |
66+
sudo ln -s /usr/bin/llvm-symbolizer-6.0 /usr/bin/llvm-symbolizer
67+
displayName: Enable debug symbols
68+
# only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945
69+
- script: |
70+
env ASAN_OPTIONS="detect_odr_violation=0" RUSTFLAGS="-Z sanitizer=address" cargo test --lib --tests --target x86_64-unknown-linux-gnu
71+
displayName: cargo -Z sanitizer=address test
72+
workingDirectory: "evmap"
73+
- job: lsan
74+
displayName: "Run leak sanitizer on test suite"
75+
pool:
76+
vmImage: ubuntu-latest
77+
steps:
78+
- template: install-rust.yml@templates
79+
parameters:
80+
rust: nightly
81+
- bash: |
82+
sudo ln -s /usr/bin/llvm-symbolizer-6.0 /usr/bin/llvm-symbolizer
83+
sed -i '/\[features\]/i [profile.dev]' Cargo.toml
84+
sed -i '/profile.dev/a opt-level = 1' Cargo.toml
85+
cat Cargo.toml
86+
displayName: Enable debug symbols
87+
- script: |
88+
env RUSTFLAGS="-Z sanitizer=leak" cargo test --target x86_64-unknown-linux-gnu
89+
displayName: cargo -Z sanitizer=leak test
90+
workingDirectory: "evmap"
7391
7492
resources:
7593
repositories:

0 commit comments

Comments
 (0)