Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 3 additions & 46 deletions Cargo.lock

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

19 changes: 4 additions & 15 deletions libs/neon-shmem/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,15 @@ license.workspace = true

[dependencies]
thiserror.workspace = true
nix.workspace = true
nix.workspace=true
workspace_hack = { version = "0.1", path = "../../workspace_hack" }
libc.workspace = true
lock_api.workspace = true
rustc-hash.workspace = true

[dev-dependencies]
criterion = { workspace = true, features = ["html_reports"] }
rand = "0.9"
rand_distr = "0.5.1"
xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
ahash.workspace = true
twox-hash = { version = "2.1.1" }
seahash = "4.1.0"
hashbrown = { git = "https://github.com/quantumish/hashbrown.git", rev = "6610e6d" }


[target.'cfg(target_os = "macos")'.dependencies]
tempfile = "3.14.0"

[[bench]]
name = "hmap_resize"
harness = false
[dev-dependencies]
rand = "0.9"
rand_distr = "0.5.1"
57 changes: 34 additions & 23 deletions libs/neon-shmem/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ pub enum HashMapShrinkError {
/// If a new process is launched with fork(), the child process inherits
/// this struct.
#[must_use]
pub struct HashMapInit<'a, K, V, S = rustc_hash::FxBuildHasher> {
pub struct HashMapInit<K: 'static, V: 'static, S = rustc_hash::FxBuildHasher> {
shmem_handle: Option<ShmemHandle>,
shared_ptr: *mut HashMapShared<'a, K, V>,
shared_ptr: *mut HashMapShared<K, V>,
shared_size: usize,
hasher: S,
num_buckets: u32,
}

impl<'a, K, V, S> Debug for HashMapInit<'a, K, V, S>
impl<K, V, S> Debug for HashMapInit<K, V, S>
where
K: Debug,
V: Debug,
Expand All @@ -79,16 +79,16 @@ where
///
/// XXX: We're not making use of it at the moment, but this struct could
/// hold process-local information in the future.
pub struct HashMapAccess<'a, K, V, S = rustc_hash::FxBuildHasher> {
pub struct HashMapAccess<K: 'static, V: 'static, S = rustc_hash::FxBuildHasher> {
shmem_handle: Option<ShmemHandle>,
shared_ptr: *mut HashMapShared<'a, K, V>,
shared_ptr: *mut HashMapShared<K, V>,
hasher: S,
}

unsafe impl<K: Sync, V: Sync, S> Sync for HashMapAccess<'_, K, V, S> {}
unsafe impl<K: Send, V: Send, S> Send for HashMapAccess<'_, K, V, S> {}
unsafe impl<K: Sync, V: Sync, S> Sync for HashMapAccess<K, V, S> {}
unsafe impl<K: Send, V: Send, S> Send for HashMapAccess<K, V, S> {}

impl<'a, K, V, S> Debug for HashMapAccess<'a, K, V, S>
impl<K, V, S> Debug for HashMapAccess<K, V, S>
where
K: Debug,
V: Debug,
Expand All @@ -102,14 +102,14 @@ where
}
}

impl<'a, K: Clone + Hash + Eq, V, S> HashMapInit<'a, K, V, S> {
impl<K: Clone + Hash + Eq + 'static, V: 'static, S> HashMapInit<K, V, S> {
/// Change the 'hasher' used by the hash table.
///
/// NOTE: This must be called right after creating the hash table,
/// before inserting any entries and before calling attach_writer/reader.
/// Otherwise different accessors could be using different hash function,
/// with confusing results.
pub fn with_hasher<T: BuildHasher>(self, hasher: T) -> HashMapInit<'a, K, V, T> {
pub fn with_hasher<T: BuildHasher>(self, hasher: T) -> HashMapInit<K, V, T> {
HashMapInit {
hasher,
shmem_handle: self.shmem_handle,
Expand Down Expand Up @@ -177,7 +177,7 @@ impl<'a, K: Clone + Hash + Eq, V, S> HashMapInit<'a, K, V, S> {
}

/// Attach to a hash table for writing.
pub fn attach_writer(self) -> HashMapAccess<'a, K, V, S> {
pub fn attach_writer(self) -> HashMapAccess<K, V, S> {
HashMapAccess {
shmem_handle: self.shmem_handle,
shared_ptr: self.shared_ptr,
Expand All @@ -189,7 +189,7 @@ impl<'a, K: Clone + Hash + Eq, V, S> HashMapInit<'a, K, V, S> {
///
/// This is a holdover from a previous implementation and is being kept around for
/// backwards compatibility reasons.
pub fn attach_reader(self) -> HashMapAccess<'a, K, V, S> {
pub fn attach_reader(self) -> HashMapAccess<K, V, S> {
self.attach_writer()
}
}
Expand All @@ -206,14 +206,14 @@ impl<'a, K: Clone + Hash + Eq, V, S> HashMapInit<'a, K, V, S> {
/// dictionary
///
/// In between the above parts, there can be padding bytes to align the parts correctly.
type HashMapShared<'a, K, V> = RwLock<CoreHashMap<'a, K, V>>;
type HashMapShared<K, V> = RwLock<CoreHashMap<K, V>>;

impl<'a, K, V> HashMapInit<'a, K, V, rustc_hash::FxBuildHasher>
impl<K, V: 'static> HashMapInit<K, V, rustc_hash::FxBuildHasher>
where
K: Clone + Hash + Eq,
K: Clone + Hash + Eq + 'static,
{
/// Place the hash table within a user-supplied fixed memory area.
pub fn with_fixed(num_buckets: u32, area: &'a mut [MaybeUninit<u8>]) -> Self {
pub fn with_fixed(num_buckets: u32, area: &'static mut [MaybeUninit<u8>]) -> Self {
Self::new(
num_buckets,
None,
Expand Down Expand Up @@ -269,17 +269,18 @@ where
}
}

impl<'a, K, V, S: BuildHasher> HashMapAccess<'a, K, V, S>
impl<K, V, S: BuildHasher> HashMapAccess<K, V, S>
where
K: Clone + Hash + Eq,
K: Clone + Hash + Eq + 'static,
V: 'static
{
/// Hash a key using the map's hasher.
#[inline]
fn get_hash_value(&self, key: &K) -> u64 {
self.hasher.hash_one(key)
}

fn entry_with_hash(&self, key: K, hash: u64) -> Entry<'a, '_, K, V> {
fn entry_with_hash(&self, key: K, hash: u64) -> Entry<'_, K, V> {
let mut map = unsafe { self.shared_ptr.as_ref() }.unwrap().write();
let dict_pos = hash as usize % map.dictionary.len();
let first = map.dictionary[dict_pos];
Expand Down Expand Up @@ -331,7 +332,7 @@ where
///
/// NB: This takes a write lock as there's no way to distinguish whether the intention
/// is to use the entry for reading or for writing in advance.
pub fn entry(&self, key: K) -> Entry<'a, '_, K, V> {
pub fn entry(&self, key: K) -> Entry<'_, K, V> {
let hash = self.get_hash_value(&key);
self.entry_with_hash(key, hash)
}
Expand Down Expand Up @@ -365,7 +366,7 @@ where
/// Has more overhead than one would intuitively expect: performs both a clone of the key
/// due to the [`OccupiedEntry`] type owning the key and also a hash of the key in order
/// to enable repairing the hash chain if the entry is removed.
pub fn entry_at_bucket(&self, pos: usize) -> Option<OccupiedEntry<'a, '_, K, V>> {
pub fn entry_at_bucket(&self, pos: usize) -> Option<OccupiedEntry<'_, K, V>> {
let map = unsafe { self.shared_ptr.as_mut() }.unwrap().write();
if pos >= map.buckets.len() {
return None;
Expand All @@ -389,6 +390,16 @@ where
map.get_num_buckets()
}

/// Returns the logical number of buckets in the table (aka the amount of allocatable buckets).
pub fn get_num_logical_buckets(&self) -> usize {
let map = unsafe { self.shared_ptr.as_ref() }.unwrap().read();
if map.alloc_limit == INVALID_POS {
map.get_num_buckets()
} else {
map.alloc_limit as usize
}
}

/// Return the key and value stored in bucket with given index. This can be used to
/// iterate through the hash map.
// TODO: An Iterator might be nicer. The communicator's clock algorithm needs to
Expand Down Expand Up @@ -430,7 +441,7 @@ where
/// in the process.
fn rehash_dict(
&self,
inner: &mut CoreHashMap<'a, K, V>,
inner: &mut CoreHashMap<K, V>,
buckets_ptr: *mut core::Bucket<K, V>,
end_ptr: *mut u8,
num_buckets: u32,
Expand Down Expand Up @@ -541,7 +552,7 @@ where
/// # Panics
/// Panics if called on a map initialized with [`HashMapInit::with_fixed`] or if `num_buckets` is
/// greater than the number of buckets in the map.
pub fn begin_shrink(&mut self, num_buckets: u32) {
pub fn begin_shrink(&self, num_buckets: u32) {
let mut map = unsafe { self.shared_ptr.as_mut() }.unwrap().write();
assert!(
num_buckets <= map.get_num_buckets() as u32,
Expand Down
14 changes: 7 additions & 7 deletions libs/neon-shmem/src/hash/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ where
}

/// Core hash table implementation.
pub(crate) struct CoreHashMap<'a, K, V> {
pub(crate) struct CoreHashMap<K: 'static, V: 'static> {
/// Dictionary used to map hashes to bucket indices.
pub(crate) dictionary: &'a mut [u32],
pub(crate) dictionary: &'static mut [u32],
/// Buckets containing key-value pairs.
pub(crate) buckets: &'a mut [Bucket<K, V>],
pub(crate) buckets: &'static mut [Bucket<K, V>],
/// Head of the freelist.
pub(crate) free_head: u32,
/// Maximum index of a bucket allowed to be allocated. [`INVALID_POS`] if no limit.
Expand All @@ -45,7 +45,7 @@ pub(crate) struct CoreHashMap<'a, K, V> {
pub(crate) buckets_in_use: u32,
}

impl<'a, K, V> Debug for CoreHashMap<'a, K, V>
impl<K, V> Debug for CoreHashMap<K, V>
where
K: Debug,
V: Debug,
Expand All @@ -65,7 +65,7 @@ where
#[derive(Debug, PartialEq)]
pub struct FullError;

impl<'a, K: Clone + Hash + Eq, V> CoreHashMap<'a, K, V> {
impl<K: Clone + Hash + Eq, V> CoreHashMap<K, V> {
const FILL_FACTOR: f32 = 0.60;

/// Estimate the size of data contained within the the hash map.
Expand All @@ -83,8 +83,8 @@ impl<'a, K: Clone + Hash + Eq, V> CoreHashMap<'a, K, V> {
}

pub fn new(
buckets: &'a mut [MaybeUninit<Bucket<K, V>>],
dictionary: &'a mut [MaybeUninit<u32>],
buckets: &'static mut [MaybeUninit<Bucket<K, V>>],
dictionary: &'static mut [MaybeUninit<u32>],
) -> Self {
// Initialize the buckets
for i in 0..buckets.len() {
Expand Down
Loading