#map #read-write #lock-free #multi-value #hash-map

evmap

一个无锁、最终一致的并发多值映射

56 个版本 (34 个稳定版)

11.0.0-alpha.72020 年 12 月 21 日
11.0.0-alpha.22020 年 11 月 29 日
11.0.0-alpha.12020 年 6 月 3 日
10.0.2 2020 年 5 月 11 日
0.4.0 2017 年 3 月 31 日

#452 in 并发

Download history 974/week @ 2024-03-13 857/week @ 2024-03-20 541/week @ 2024-03-27 658/week @ 2024-04-03 727/week @ 2024-04-10 750/week @ 2024-04-17 1023/week @ 2024-04-24 1164/week @ 2024-05-01 690/week @ 2024-05-08 740/week @ 2024-05-15 964/week @ 2024-05-22 766/week @ 2024-05-29 1125/week @ 2024-06-05 1217/week @ 2024-06-12 1169/week @ 2024-06-19 2337/week @ 2024-06-26

5,985 每月下载量
用于 22 个 Crates (16 个直接使用)

MIT/Apache

89KB
1.5K SLoC

Build Status Codecov Crates.io Documentation

一个无锁、最终一致的并发多值映射。

此映射实现允许读取和写入完全并行执行,没有隐式同步开销。读取永远不会在其关键路径上获取锁,写入也是如此,假设有一个单写入者(使用 Mutex 可以实现多写入者),这在竞争条件下显著提高了性能。它由并发原语 left-right 支持。

此模块暴露的权衡是最终一致性:除了跟随显式同步之外,写入对读取者不可见。具体来说,读者只能看到写入者在最后一次调用 WriteHandle::flush 之前的操作。这允许写入者决定它们愿意让读取变得多旧。它们可以在每次写入后刷新映射以模拟常规并发 HashMap,或者它们可以偶尔刷新以减少同步开销,但以读取过时为代价。

对于读取密集型的工作负载,此模块使用的方案特别有用。写入者可以承受在每次写入后刷新,这提供了最新的读取,而读取者保持快速,因为它们永远不需要获取锁。

此映射是多值的,这意味着每个键映射到一个 集合 的值。这通过为每个值添加一层通过 Vec 的间接层来增加一些内存成本,但允许更高级的使用。这个选择是因为不可能在这个映射的语义之上模拟这种功能(想想看——操作日志将包含什么?)。

为了便于更高级的使用场景,这两个映射也携带一些可定制的元信息。写入者可以随意更新这些信息,当刷新发生时,当前的元信息也会对读取者可见。例如,这可以用来指示刷新发生的时间。

性能

这些基准测试已经过时,但传达了正确的观点。希望我很快有机会再次更新它们。

我在一个带有英特尔(R) Xeon(R) CPU E5-2660 v3 @ 2.60GHz CPU的40核机器上,使用位于benchmark/目录下的二进制文件,对evmap进行了基准测试,并与由读写锁保护的Rust标准库中的HashMap进行了比较,以及与chashmap——一个提供基于桶级别多读锁的“并发哈希表”的crate进行了比较。

基准测试运行了一组紧密循环中的读线程和写线程,每个线程分别对映射中的随机键进行读取或写入。下面提供了均匀分布和偏斜分布的结果。该基准测试测量随着读者和作者数量的增加,每秒平均读取和写入次数。

初步结果表明,在竞争条件下,evmap的性能良好,特别是在读取方面。这个基准测试代表了evmap的最坏情况使用,其中每个写入都会执行一个refresh。如果映射刷新得更少,性能会提高(见底部图形)。

Read throughput Write throughput Write throughput

依赖关系

~0.2–27MB
~340K SLoC