GithubHelp home page GithubHelp logo

robclu / leapfrog Goto Github PK

View Code? Open in Web Editor NEW
188.0 1.0 8.0 195 KB

Lock-free concurrent and single-threaded hash map implementations using Leapfrog probing. Currently the highest performance concurrent HashMap in Rust for certain use cases.

License: Apache License 2.0

Rust 100.00%
concurrency rust hashmap data-structures concurrent-hashmap hashmaps lock-free-hashtable

leapfrog's Introduction

version documentation

Leapfrog

The leapfrog crate contains two hash map implementations, HashMap, which is a single-threaded HashMap, and LeapMap, which is fast, concurrent version of the HashMap, where all operations can be performed concurrently from any number of threads. If the key and value types support atomic operations then the map is lock-free, and when there is no atomic support for either the key or value, then operations within the map on that non-atomic type use an efficient spinlock and the locking is very fine-grained.

The performance of both maps is good on the benchmarks it's been tested on. The HashMap is between 1.2 and 1.5x the std::collections::HashMap. Compared to RwLock<HashMap>, on 16 cores the LeapMap is around 13.5x faster. It also scales very well up to high core counts, for multiple workloads. Benchmark results can be found at rust hashmap benchmarks. These bechmarks, however, are limited in the use cases that they cover, and should be extended to cover a much wider scope. Nevertheless, for those bechmarks, the LeapMap is the fastest map.

For more details on the API and the differences compared to std::collections::HashMap, please see the crate documentation.

Recent Changes

The implementations of these maps have recently been updated to store keys, so the API for both is much closer to std::collections::HashMap. There is now also support for serde with the optional "serde" feature. The maps also now have iterator support. The default hasher has been changed to be the DOS resistant hasher from the standard library.

The "stable_alloc" feature flag has been added to allow the crate to be used with the stable toolchain.

Current Limitations

These is not yet support for rayon, and there are not yet Set versions of the Maps.

Performance

A snapshot of the results for the LeapMap on a read heavy workload using a 16 core AMD 3950x CPU are the following (with throughput in Mops/second, cores in brackets, and performance realtive to std::collections::HashMap with RwLock):

Map Throughput (1) Relative (1) Throughput (16) Relative (16)
RwLock + HashMap 19.4 1.0 11.7 0.60
Flurry 11.2 0.58 80.8 4.16
DashMap 14.1 0.72 87.5 4.51
LeapMap 17.8 0.92 148.0 7.62

On a 12-core M2 Max the results are the following (again values are all relative to std::collections::HashMap):

Map Throughput (1) Relative (1) Throughput (12) Relative (12)
RwLock + HashMap 21.2 1.0 3.4 0.16
Flurry 8.0 0.37 64.5 3.04
DashMap 10.4 0.49 42.2 2.00
Evmap 13.5 0.64 18.7 0.88
LeapMap 13.9 0.66 106.1 5.00

On benchmarks of other use cases, leapfrog's LeapMap is faster by even higher factors.

For the single threaded leapfrog HashMap, a benchmark of random inserts and deletes (a port of this benchmark of C++ hashmaps) has the rust std HashMap at around 40Mops/s and the leapfrog HashMap at around 50Mops/s, with default hasher from the standard library, which are competitive with the C++ hash maps from the linked benchmark.

Probing

Both maps use the same leapfrog probing strategy, for which you can find more information on Jeff Preshing's leapfrog probing blog post. A c++ implementation of the map can be found at his Junction library, which was the starting point for this implementation. Those maps don't have support for keys and therefore collisions, which these maps do. The probing strategy requires 8 bytes per key-value pair and and additional 8 bytes are stored for the hashed key so that it doesn't have to be rehashed for comparisons.

Features

There is optional support for serde, via the "serde" feature.

Usage with stable

This crate requires the allocator_api feature, which is only available on nightly. To enable use of the crate with the stable toolchain, the "stable_alloc" feature has been added.

If/when the allocator_api feature is no longer experimental, this feature flag will be removed.

License

This crate is licensed under either the Apache License, Version 2.0, see here or here or the MIT license see here or here, at your chosing. It can be used freely for both commercial and non-commercial applications, so long as you publish the contents of your chosen license file somewhere in your documentation.

leapfrog's People

Contributors

mosmeh avatar robclu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

leapfrog's Issues

Suggestion for addition to library

With the requirement for values to implement Value, typically through the use of sentinel values, hard-to-diagnose bugs can crop up.

The suggestion is an addition of a type to the library; an enum using generics with three variants: a redirect variant, a null variant, and a variant which actually contains data. Developers using your library can use this enum, and those with very strict space or performance requirements can implement Value manually.

A sample implementation:

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LeapValue<V> {
    Redirect,
    Null,
    Inner(V)
}

impl<V> LeapValue<V> {
	pub fn to_option(self) -> Option<V> {
        match self {
            Self::Inner(inner) => Some(inner),
            _ => None,
        }
    }
}

impl<V> Value for LeapValue<V>
where V: Sized + Debug + Clone + Copy {
    fn is_redirect(&self) -> bool {
        match self {
            Self::Redirect => true,
            _ => false,
        }
    }

    fn is_null(&self) -> bool {
        match self {
            Self::Null => true,
            _ => false,
        }
    }

    fn redirect() -> Self {
        Self::Redirect
    }

    fn null() -> Self {
        Self::Null
    }
}

What do you think?

Miri Complains of OOB.

Hi. Thanks for an interesting data structure. If you don't mind, I wanted to check on something with you.

As of 659a035, the Miri checker complains during cargo miri test hashmap_insert that a pointer access is out of bounds during one of the test cases.

It could be Miri being flaky for a randomized test case, but It would be good to know.

Full log below:

    Finished test [unoptimized + debuginfo] target(s) in 0.05s
     Running unittests (target/miri/x86_64-unknown-linux-gnu/debug/deps/leapfrog-fcf5d41e16f0fa9c)
     Running tests/basic.rs (target/miri/x86_64-unknown-linux-gnu/debug/deps/basic-164949aba478b7f0)
     Running tests/cuckoo.rs (target/miri/x86_64-unknown-linux-gnu/debug/deps/cuckoo-753b178d576a0e37)
     Running tests/hashmap.rs (target/miri/x86_64-unknown-linux-gnu/debug/deps/hashmap-e03e0f105210a369)
error: Undefined Behavior: dereferencing pointer failed: alloc67619 has size 53248, so pointer to 212992 bytes starting at offset 0 is out-of-bounds
   --> /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/raw.rs:130:14
    |
130 |     unsafe { &mut *ptr::slice_from_raw_parts_mut(data, len) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: alloc67619 has size 53248, so pointer to 212992 bytes starting at offset 0 is out-of-bounds
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
            
    = note: inside `std::slice::from_raw_parts_mut::<leapfrog::hashmap::Bucket<u64, u64>>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/raw.rs:130:14
    = note: inside `leapfrog::hashmap::Table::<u64, u64>::bucket_slice_mut` at /home/ubuntu/target-code/auto-crawl/leapfrog/src/hashmap.rs:927:18
    = note: inside `leapfrog::HashMap::<u64, u64>::insert` at /home/ubuntu/target-code/auto-crawl/leapfrog/src/hashmap.rs:183:27
note: inside `hashmap_insert` at tests/hashmap.rs:34:33
   --> tests/hashmap.rs:34:33
    |
34  |             if let Some(_old) = map.insert(key, key) {
    |                                 ^^^^^^^^^^^^^^^^^^^^
note: inside closure at tests/hashmap.rs:16:1
   --> tests/hashmap.rs:16:1
    |
15  |   #[test]
    |   ------- in this procedural macro expansion
16  | / fn hashmap_insert() {
17  | |     let mut map = HashMap::<u64, u64>::with_capacity(KEYS_TO_INSERT);
18  | |
19  | |     let mut rng = thread_rng();
...   |
70  | |     assert_eq!(inserted, removed);
71  | | }
    | |_^
    = note: inside `<[closure@tests/hashmap.rs:16:1: 71:2] as std::ops::FnOnce<()>>::call_once - shim` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
    = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
    = note: inside `test::__rust_begin_short_backtrace::<fn()>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:585:5
    = note: inside closure at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:576:30
    = note: inside `<[closure@test::run_test::{closure#2}] as std::ops::FnOnce<()>>::call_once - shim(vtable)` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
    = note: inside `<std::boxed::Box<dyn std::ops::FnOnce() + std::marker::Send> as std::ops::FnOnce<()>>::call_once` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:1811:9
    = note: inside `<std::panic::AssertUnwindSafe<std::boxed::Box<dyn std::ops::FnOnce() + std::marker::Send>> as std::ops::FnOnce<()>>::call_once` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:271:9
    = note: inside `std::panicking::r#try::do_call::<std::panic::AssertUnwindSafe<std::boxed::Box<dyn std::ops::FnOnce() + std::marker::Send>>, ()>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
    = note: inside `std::panicking::r#try::<(), std::panic::AssertUnwindSafe<std::boxed::Box<dyn std::ops::FnOnce() + std::marker::Send>>>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
    = note: inside `std::panic::catch_unwind::<std::panic::AssertUnwindSafe<std::boxed::Box<dyn std::ops::FnOnce() + std::marker::Send>>, ()>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
    = note: inside `test::run_test_in_process` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:608:18
    = note: inside closure at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:500:39
    = note: inside `test::run_test::run_test_inner` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:538:13
    = note: inside `test::run_test` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:572:28
    = note: inside `test::run_tests::<[closure@test::run_tests_console::{closure#2}]>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:313:17
    = note: inside `test::run_tests_console` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/console.rs:290:5
    = note: inside `test::test_main` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:124:15
    = note: inside `test::test_main_static` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test/src/lib.rs:143:5
    = note: inside `main`
    = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
    = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:123:18
    = note: inside closure at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:145:18
    = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13
    = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
    = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
    = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
    = note: inside closure at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:48
    = note: inside `std::panicking::r#try::do_call::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
    = note: inside `std::panicking::r#try::<isize, [closure@std::rt::lang_start_internal::{closure#2}]>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
    = note: inside `std::panic::catch_unwind::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
    = note: inside `std::rt::lang_start_internal` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:20
    = note: inside `std::rt::lang_start::<()>` at /home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:144:17
    = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

error: test failed, to rerun pass '--test hashmap'

Available on the stable release channel?

Does Leapfrog require nightly rust only?

error[E0554]: `#![feature]` may not be used on the stable release channel
   --> C:\Users\Mikur\.cargo\registry\src\github.com-1ecc6299db9ec823\leapfrog-0.2.2\src\lib.rs:133:12
    |
133 | #![feature(allocator_api)]
    |            ^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0554`.
error: could not compile `leapfrog` due to previous error

Documentation and presentation are misleading and dangerous

GitHub

Lock-free concurrent and single-threaded hash map implementations using Leapfrog probing. Currently the highest performance concurrent HashMap in Rust.

README

The leapfrog crate contains two HashMap implementations, HashMap, which is a single-threaded HashMap, which can be used as an alternative to rusts built in HashMap (without somewhat limited functionality but better performance),

Emphasis added by me. I assume the without is meant to be with.

In bold:

If the value type for the map supports atomic operations then this map will not lock, while if the value type does not support atomic perations then accessing the value uses an efficient spinlock implementation.

500 words before:

For a more detailed description of the limitations and behaviour of the maps, see the crate documentation:

Docs.rs

500 words before:

You should use a hasher which produces unique hashes for each key. As only hashes are stored, if two keys hash to the same hash then the value for the keys which hash to the same value my be overwritten by each other if they are inserted concurrently.

What about the single threaded 'HashMap'? If I get a key hash collision, it won't overwrite the previous value?


Ignoring the factual errors, and assumed typos. The crate presents itself as this amazing super fast HashMap and spends most of its time talking about performance. When in reality it avoids the hardest problem of HashMaps, key collision, by just ignoring it. Effectively all hash functions will produce collisions at some point, in some scenario. Testing with some data and saying it works is dangerous. For example here is the fastest http download function in Rust:

fn download(_url: &str) -> String {
    "404".into()
}

I tried it on a buch of websites and it is thousands of times faster than hyper. To correctly use this leapfrog data structure, which I wouldn't even call HashMap, a user has to guarantee that either:

A) They are sure that under no circumstances ever, a collision will happen
B) Losing data to collisions is fine

I guarantee you, most existing users of the stdlib HashMap, can't and don't want to guarantee either.


Given all the above, I worry that users will use this project without realizing the implications, only to get nasty bugs later on. Rust is all about avoiding that. I suggest you make it clear that this is not a regular hash map and comes with some serious implications and can't be used as alternative to the stdlib HashMap or other concurrent HashMaps without serious consideration. Here are some ways you could help future users avoid getting nasty surprises:

  • Change the description to something like 'Lock-free concurrent and single-threaded hash map implementations using Leapfrog probing.' It's unfair to compare it to other HashMaps without context. You wouldn't publish a Vec crate and claim it is faster than any other linked list implementation in Rust.
  • Right at the beginning of the README and docs.rs mention in bold that leapfrog will ignore key collisions and will lose data if used with the default hasher or any other non perfect hash function. You should probably also mention that in contrast to the stdlib HashMap, the default hasher is not DOS resistant.
  • In the performance comparisons, emphasize that this is not a fair comparison, as the things you compare it to, implement a different contract.

Leapfrog insert() keeps spinning in endless probe loop

Hi there, I have a reproducible case of where Leapfrog map is endlessly going in a probing loop during inserts.
MacBook Pro 2021, M1 Pro, OSX 12.1

Repo and commit is here (branch is try-leapfrog):
velvia/ying-profiler@cec84d0

To reproduce, run cargo test stress on nightly. The test will seem to hang. Use gdb/lldb to attach to the test process. You will find a stack trace like this:

* thread #3
  * frame #0: 0x0000000104481338 allocation_tests-de2cce5ea52d024e`leapfrog::leapmap::get_cell::ha8ca75b7371b4c44(buckets=&[leapfrog::leapmap::Bucket<u64, ying_profiler::AllocInfo>] @ 0x000000016bf31660, index=5117530326985502155, size_mask=5371614424) at leapmap.rs:1281
    frame #1: 0x000000010447d4ac allocation_tests-de2cce5ea52d024e`leapfrog::leapmap::LeapMap$LT$K$C$V$C$H$C$A$GT$::insert_or_find::hec008331e7fccd14(hash=5117530326985502029, key=0x000000016bf31a10, value=(__0 = 8198732939394532628, __1 = 1668144508664), buckets=&[leapfrog::leapmap::Bucket<u64, ying_profiler::AllocInfo>] @ 0x000000016bf31908, size_mask=4095) at leapmap.rs:1046:32
    frame #2: 0x0000000104480ca0 allocation_tests-de2cce5ea52d024e`leapfrog::leapmap::LeapMap$LT$K$C$V$C$H$C$A$GT$::insert::he4b7beadef7377c2(self=0x00000001049580e8, key=5561700864, value=(__0 = 8198732939394532628, __1 = 1668144508664)) at leapmap.rs:369:19
    frame #3: 0x000000010445c1dc allocation_tests-de2cce5ea52d024e`_$LT$ying_profiler..YingProfiler$u20$as$u20$core..alloc..global..GlobalAlloc$GT$::realloc::_$u7b$$u7b$closure$u7d$$u7d$::haa9806d619e34592(tl_state=0x000000013af127e8) at lib.rs:511:29
    frame #4: 0x0000000104469074 allocation_tests-de2cce5ea52d024e`std::thread::local::LocalKey$LT$T$GT$::try_with::h6434fb8e59e19aff(self=0x0000000104924920, f={closure_env#0} @ 0x000000016bf31dc0) at local.rs:446:16
    frame #5: 0x0000000104468a38 allocation_tests-de2cce5ea52d024e`std::thread::local::LocalKey$LT$T$GT$::with::hb9d8fb8bc986e5c2(self=0x0000000104924920, f=<unavailable>) at local.rs:422:9

I have attached a log of my lldb debug session, which shows that the probing code seems to keep going in a loop. It is as though it keeps finding null values but keeps going for some reason, I don't quite understand it.

ying-leapmap-probe-stuck.lldb-session.log

Any help would be really appreciated. The calling code to LeapMap is here, it is for insert with value type AllocStats which is basically a (u64, u64) type class/tuple. Perhaps there is something wrong with the way I'm defining a Value for it?

https://github.com/velvia/ying-profiler/blob/4e8cfbe9e547adb9de5750a8d7258b918e6d0ecd/src/lib.rs#L511

Note that I'm uniquely interested in this project because of the lock-free nature is important for this custom memory profiler I'm writing, or at least the lack of using traditional Mutex/locks, plus the freedom to allocate using alternative (in my case, the base System) allocators.

Can you confirm leapfrog is now a drop-in replacement for the std hashmaps?

Hi,

Just saw this:

https://www.reddit.com/r/rust/comments/shw313/comment/hv5b0uz/

The primary limitation of the map is that it does not store keys, but rather hashes of the keys. Thus far this has not been a limitation, however, it would be possible to add key support, if neccessary.

That means that leapfrog isn't usable as drop-in replacement for the hashmaps it is compared to.

Just want to confirm this key issue has been fixed.

Thanks.

Inserting fails unpredictably with structs & alignment

For some reason, inserting will either freeze or will not properly insert if the struct isn't a multiple of 64 bits in size.

See code below:

If you change b to a u64, the code works as expected.

use std::fmt::{Debug};
use lazy_static::lazy_static;
use leapfrog::{LeapMap, Value};


#[derive(Debug, Copy, Clone, PartialEq)]
struct TestStruct {
    a: u64,
    b: u32,
}

macro_rules! value_impl {
    ($type:ty, $redirect_expr:expr, $null_expr:expr) => {
        impl Value for $type {
            #[inline]
            fn is_redirect(&self) -> bool {
                *self == $redirect_expr
            }
            #[inline]
            fn is_null(&self) -> bool {
                *self == $null_expr
            }
            #[inline]
            fn redirect() -> Self {
                $redirect_expr
            }
            #[inline]
            fn null() -> Self {
                $null_expr
            }
        }
    };
}

value_impl!(TestStruct, TestStruct {a: u64::MAX, b: 0 }, TestStruct {a: u64::MAX - 1, b: 0});

lazy_static! {
    static ref A_MAP: LeapMap<u64, TestStruct> = LeapMap::new();
    static ref B_MAP: LeapMap<u64, u64> = LeapMap::new();
}

fn main() {
    let C_MAP: LeapMap<u64, TestStruct> = LeapMap::new();
    let D_MAP: LeapMap<u64, u64> = LeapMap::new();

    let mut key = 0;
    for i in 0..100 {
        key += 1;
        let result = A_MAP.insert(key, TestStruct { a: key, b: 0});
        println!("Inserting new key {} sz {} res {:?}", key, A_MAP.len(), result);
        let result = C_MAP.insert(key, TestStruct { a: key, b: 0});
        println!("Inserting new key {} sz {} res {:?}", key, C_MAP.len(), result);
        let result = B_MAP.insert(key, key);
        println!("Inserting new key {} sz {} res {:?}", key, B_MAP.len(), result);
        let result = D_MAP.insert(key, key);
        println!("Inserting new key {} sz {} res {:?}", key, D_MAP.len(), result);

    }

    println!("Hello, world!");
}

Inserting causes a pianic

Ran with one thread, haven't made efforts on reproducing but it appears to happen when my system is under heavy load. Also occurs when running with multiple threads.

thread 'main' panicked at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/leapfrog-0.3.0/src/leapmap.rs:783:13:
assertion failed: migrator.stale_sources[index].load(Ordering::Relaxed).is_null()
stack backtrace:
   0: rust_begin_unwind
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:144:5
   3: leapfrog::leapmap::LeapMap<K,V,H,A>::initialize_migrator
             at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/leapfrog-0.3.0/src/leapmap.rs:783:13
   4: leapfrog::leapmap::LeapMap<K,V,H,A>::insert
             at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/leapfrog-0.3.0/src/leapmap.rs:386:25

Clone

Is there a specific reason as to why the single-threaded hash map does not implement Clone nor Default?

Serde support

Dashmap supports serde with the optional serde feature.
Is this a feature that is out of scope for this crate or is there a chance it can be added?

iter() yields invalid items after they are remove()d

Code

fn main() {
    let map = leapfrog::LeapMap::new();
    assert!(map.insert(42, 123).is_none());
    assert!(map.remove(&42).is_some());
    assert!(map.remove(&42).is_none());
    assert!(map.get(&42).is_none());
    for mut x in map.iter() {
        dbg!(x.key_value());
    }
}

Expected output

Nothing or None

Actual output

[src/main.rs:8] x.key_value() = Some(
    (
        42,
        2147483646,
    ),
)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.