5个稳定版本
新 1.0.4 | 2024年8月13日 |
---|---|
1.0.3 | 2024年7月10日 |
1.0.2 | 2023年9月29日 |
1.0.1 | 2023年6月21日 |
1.0.0 | 2023年6月15日 |
#77 在 操作系统 类别中
每月4,909次下载
42KB
551 行
mmap-sync
mmap-sync
是一个Rust crate,旨在管理单个写入进程和多个读取进程之间的高性能、并发数据访问,利用内存映射文件、无等待同步和零拷贝反序列化的优势。我们在博客文章中使用了 mmap-sync
用于大规模机器学习: "每个请求,每个微秒:在Cloudflare上的可扩展机器学习"。
概述
mmap-sync
的核心是一个 Synchronizer
结构,它提供了一个简单的接口,具有 "写入" 和 "读取" 方法,允许用户读取和写入实现或派生某些 rkyv 特性的任何 Rust struct (T
)。
impl Synchronizer {
/// Write a given `entity` into the next available memory mapped file.
pub fn write<T>(&mut self, entity: &T, grace_duration: Duration) -> Result<(usize, bool), SynchronizerError> {
…
}
/// Reads and returns `entity` struct from mapped memory wrapped in `ReadResult`
pub fn read<T>(&mut self) -> Result<ReadResult<T>, SynchronizerError> {
…
}
}
数据存储在共享映射内存中,允许 Synchronizer
同时从其中 "写入" 和 "读取"。这使得 mmap-sync
成为一个高效且灵活的工具,用于管理共享的并发数据访问。
映射内存
使用内存映射文件比其他进程间通信 (IPC) 机制具有几个优点。它允许不同的进程访问相同的内存空间,绕过昂贵的序列化和反序列化需求。这种设计使 mmap-sync
能够在进程间提供极快、低开销的数据共享。
无等待同步
我们无等待的数据访问模式借鉴了来自 Linux内核的读-复制-更新 (RCU) 模式 和 左右并发控制技术 的灵感。在我们的解决方案中,我们在不同的内存映射文件中维护数据的两份副本。对数据的写入由单个写入者管理,多个读取者能够并发访问数据。
我们将在一个称为“状态”的第三方内存映射文件中存储同步状态,该状态协调对数据副本的访问。该文件包含一个原子64位整数,代表一个InstanceVersion
和一对额外的原子32位变量,用于跟踪每个数据副本的活跃读取器数量。InstanceVersion
由当前活跃的数据文件索引(1位)、数据大小(39位,可容纳数据大小高达549 GB)和数据校验和(24位)组成。
零拷贝反序列化
为了高效地存储和检索数据,mmap-sync
利用rkyv库的帮助,直接引用序列化形式的字节。这显著减少了访问和使用数据所需的时间和内存。为Synchronizer
提供的模板类型T
可以是任何实现了指定rkyv
特质的Rust结构体。
入门指南
要使用mmap-sync
,将其添加到您的Cargo.toml
中的[dependencies]
[dependencies]
mmap-sync = "1"
然后,在您的Rust程序中导入mmap-sync
use mmap_sync::synchronizer::Synchronizer;
查看提供的示例以获取详细用法
这些示例共享一个公共模块,该模块定义了要写入和读取的数据结构。
要运行这些示例,请按照以下步骤操作
- 打开终端并导航到您的项目目录。
- 使用以下命令执行写入示例:
cargo run --example writer
。 - 同样,使用以下命令运行读取示例:
cargo run --example reader
。
在成功执行这些示例后,终端输出应类似于
# Writer example
written: 36 bytes | reset: false
# Reader example
version: 7 messages: ["Hello", "World", "!"]
此外,还将创建以下文件
$ stat -c '%A %s %n' /tmp/hello_world_*
-rw-r----- 36 /tmp/hello_world_data_0
-rw-r----- 36 /tmp/hello_world_data_1
-rw-rw---- 16 /tmp/hello_world_state
按照这些步骤,您可以在Rust应用程序中开始利用mmap-sync
进行高效的进程间并发数据访问。
性能调整
使用tmpfs
卷将减少磁盘I/O延迟,因为它直接在RAM上运行,与传统的基于磁盘的存储相比,提供更快的读写能力
let mut synchronizer = Synchronizer::new("/dev/shm/hello_world".as_ref());
此更改将同步器指向位于tmpfs
文件系统中的共享内存对象,该文件系统通常在大多数Linux系统中挂载在/dev/shm
。这应该有助于缓解与磁盘I/O相关的部分瓶颈。如果/dev/shm
没有足够的空间或您想要创建一个专用的tmpfs
实例,您可以使用以下命令设置自己的实例。例如,要创建一个1GB的tmpfs
卷,您可以使用以下命令
sudo mount -t tmpfs -o size=1G tmpfs /mnt/mytmpfs
基准测试
要运行基准测试,您首先需要安装cargo-criterion
二进制文件
cargo install cargo-criterion
然后您将能够使用以下命令运行基准测试
cargo criterion --bench synchronizer
以下基准测试是在搭载第 13 代英特尔Core(R) i7-13800H 处理器的 Linux 笔记本上执行的,编译器标志设置为 RUSTFLAGS=-C target-cpu=native
。
synchronizer/write
time: [250.71 ns 251.42 ns 252.41 ns]
thrpt: [3.9619 Melem/s 3.9774 Melem/s 3.9887 Melem/s]
synchronizer/write_raw
time: [145.25 ns 145.53 ns 145.92 ns]
thrpt: [6.8531 Melem/s 6.8717 Melem/s 6.8849 Melem/s]
synchronizer/read/check_bytes_true
time: [40.114 ns 40.139 ns 40.186 ns]
thrpt: [24.884 Melem/s 24.914 Melem/s 24.929 Melem/s]
synchronizer/read/check_bytes_false
time: [26.658 ns 26.673 ns 26.696 ns]
thrpt: [37.458 Melem/s 37.491 Melem/s 37.512 Melem/s]
依赖项
约 2.5MB
约 61K SLoC