Skip to content

Commit

Permalink
cache-align the shards to improve throughput (#303)
Browse files Browse the repository at this point in the history
* cache-align the shards to improve throughput

* fmt

* fix ci

* fix features
  • Loading branch information
conradludgate authored Jun 17, 2024
1 parent 626b98d commit 92d64ba
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
# --
# install cross
echo "installing cross"
cargo install cross
cargo install cross --locked --version 0.2.5
# --
- name: test
run: cross test --target ${{ matrix.target }}
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ cfg-if = "1.0.0"
rayon = { version = "1.7.0", optional = true }
once_cell = "1.18.0"
arbitrary = { version = "1.3.0", optional = true }
crossbeam-utils = "0.8"

[package.metadata.docs.rs]
features = ["rayon", "raw-api", "serde"]
24 changes: 15 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use core::fmt;
use core::hash::{BuildHasher, Hash, Hasher};
use core::iter::FromIterator;
use core::ops::{BitAnd, BitOr, Shl, Shr, Sub};
use crossbeam_utils::CachePadded;
use iter::{Iter, IterMut, OwningIter};
use mapref::entry::{Entry, OccupiedEntry, VacantEntry};
use mapref::multiple::RefMulti;
Expand Down Expand Up @@ -87,7 +88,7 @@ fn ncb(shard_amount: usize) -> usize {
/// This means that it is safe to ignore it across multiple threads.
pub struct DashMap<K, V, S = RandomState> {
shift: usize,
shards: Box<[RwLock<HashMap<K, V, S>>]>,
shards: Box<[CachePadded<RwLock<HashMap<K, V, S>>>]>,
hasher: S,
}

Expand All @@ -98,7 +99,7 @@ impl<K: Eq + Hash + Clone, V: Clone, S: Clone> Clone for DashMap<K, V, S> {
for shard in self.shards.iter() {
let shard = shard.read();

inner_shards.push(RwLock::new((*shard).clone()));
inner_shards.push(CachePadded::new(RwLock::new((*shard).clone())));
}

Self {
Expand Down Expand Up @@ -282,7 +283,12 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
let cps = capacity / shard_amount;

let shards = (0..shard_amount)
.map(|_| RwLock::new(HashMap::with_capacity_and_hasher(cps, hasher.clone())))
.map(|_| {
CachePadded::new(RwLock::new(HashMap::with_capacity_and_hasher(
cps,
hasher.clone(),
)))
})
.collect();

Self {
Expand Down Expand Up @@ -317,7 +323,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// let map = DashMap::<(), ()>::new();
/// println!("Amount of shards: {}", map.shards().len());
/// ```
pub fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
pub fn shards(&self) -> &[CachePadded<RwLock<HashMap<K, V, S>>>] {
&self.shards
}

Expand All @@ -337,7 +343,7 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// map.shards_mut()[shard_ind].get_mut().insert(42, SharedValue::new("forty two"));
/// assert_eq!(*map.get(&42).unwrap(), "forty two");
/// ```
pub fn shards_mut(&mut self) -> &mut [RwLock<HashMap<K, V, S>>] {
pub fn shards_mut(&mut self) -> &mut [CachePadded<RwLock<HashMap<K, V, S>>>] {
&mut self.shards
}

Expand All @@ -347,22 +353,22 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> DashMap<K, V, S> {
/// Requires the `raw-api` feature to be enabled.
///
/// See [`DashMap::shards()`] and [`DashMap::shards_mut()`] for more information.
pub fn into_shards(self) -> Box<[RwLock<HashMap<K, V, S>>]> {
pub fn into_shards(self) -> Box<[CachePadded<RwLock<HashMap<K, V, S>>>]> {
self.shards
}
} else {
#[allow(dead_code)]
pub(crate) fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
pub(crate) fn shards(&self) -> &[CachePadded<RwLock<HashMap<K, V, S>>>] {
&self.shards
}

#[allow(dead_code)]
pub(crate) fn shards_mut(&mut self) -> &mut [RwLock<HashMap<K, V, S>>] {
pub(crate) fn shards_mut(&mut self) -> &mut [CachePadded<RwLock<HashMap<K, V, S>>>] {
&mut self.shards
}

#[allow(dead_code)]
pub(crate) fn into_shards(self) -> Box<[RwLock<HashMap<K, V, S>>]> {
pub(crate) fn into_shards(self) -> Box<[CachePadded<RwLock<HashMap<K, V, S>>>]> {
self.shards
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/rayon/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::mapref::multiple::{RefMulti, RefMutMulti};
use crate::util;
use crate::{DashMap, HashMap};
use core::hash::{BuildHasher, Hash};
use crossbeam_utils::CachePadded;
use rayon::iter::plumbing::UnindexedConsumer;
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelExtend, ParallelIterator};
use std::collections::hash_map::RandomState;
Expand Down Expand Up @@ -80,7 +81,7 @@ where
}

pub struct OwningIter<K, V, S = RandomState> {
pub(super) shards: Box<[RwLock<HashMap<K, V, S>>]>,
pub(super) shards: Box<[CachePadded<RwLock<HashMap<K, V, S>>>]>,
}

impl<K, V, S> ParallelIterator for OwningIter<K, V, S>
Expand All @@ -99,6 +100,7 @@ where
.into_par_iter()
.flat_map_iter(|shard| {
shard
.into_inner()
.into_inner()
.into_iter()
.map(|(k, v)| (k, v.into_inner()))
Expand All @@ -125,7 +127,7 @@ where
}

pub struct Iter<'a, K, V, S = RandomState> {
pub(super) shards: &'a [RwLock<HashMap<K, V, S>>],
pub(super) shards: &'a [CachePadded<RwLock<HashMap<K, V, S>>>],
}

impl<'a, K, V, S> ParallelIterator for Iter<'a, K, V, S>
Expand Down Expand Up @@ -188,7 +190,7 @@ where
}

pub struct IterMut<'a, K, V, S = RandomState> {
shards: &'a [RwLock<HashMap<K, V, S>>],
shards: &'a [CachePadded<RwLock<HashMap<K, V, S>>>],
}

impl<'a, K, V, S> ParallelIterator for IterMut<'a, K, V, S>
Expand Down
5 changes: 3 additions & 2 deletions src/read_only.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use cfg_if::cfg_if;
use core::borrow::Borrow;
use core::fmt;
use core::hash::{BuildHasher, Hash};
use crossbeam_utils::CachePadded;
use std::collections::hash_map::RandomState;

/// A read-only view into a `DashMap`. Allows to obtain raw references to the stored values.
Expand Down Expand Up @@ -139,12 +140,12 @@ impl<'a, K: 'a + Eq + Hash, V: 'a, S: BuildHasher + Clone> ReadOnlyView<K, V, S>
/// let map = DashMap::<(), ()>::new().into_read_only();
/// println!("Amount of shards: {}", map.shards().len());
/// ```
pub fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
pub fn shards(&self) -> &[CachePadded<RwLock<HashMap<K, V, S>>>] {
&self.map.shards
}
} else {
#[allow(dead_code)]
pub(crate) fn shards(&self) -> &[RwLock<HashMap<K, V, S>>] {
pub(crate) fn shards(&self) -> &[CachePadded<RwLock<HashMap<K, V, S>>>] {
&self.map.shards
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use core::borrow::Borrow;
use core::fmt;
use core::hash::{BuildHasher, Hash};
use core::iter::FromIterator;
#[cfg(feature = "raw-api")]
use crossbeam_utils::CachePadded;
use std::collections::hash_map::RandomState;

/// DashSet is a thin wrapper around [`DashMap`] using `()` as the value type. It uses
Expand Down Expand Up @@ -136,7 +138,7 @@ impl<'a, K: 'a + Eq + Hash, S: BuildHasher + Clone> DashSet<K, S> {
/// let set = DashSet::<()>::new();
/// println!("Amount of shards: {}", set.shards().len());
/// ```
pub fn shards(&self) -> &[RwLock<HashMap<K, (), S>>] {
pub fn shards(&self) -> &[CachePadded<RwLock<HashMap<K, (), S>>>] {
self.inner.shards()
}
}
Expand Down

0 comments on commit 92d64ba

Please sign in to comment.