Rust.cc

Rust.cc -

Rust使用Mmap榨干机器性能极限(1)

Rust使用Mmap榨干机器性能极限(1) 事情开始于下面的数据,学过计算机体系结构课程的大致都能理解: CPU 1级缓存 .................... 0.5ns 分支预测 ....................... 5ns CPU 2级缓存 .................... 7ns 互斥锁 加锁/释放 ................ 25ns 主内存 ......................... 100ns 压缩1K数据 ..................... 3us 在1G带宽中发送2K数据 ............. 20us SSD硬盘随机读取 ................. 150us 主内存中顺序1M数据 ............... 250us 同一个数据中心来回 ............... 0.5ms SSD中顺序读取1M数据 ............. 1ms 硬盘寻址 ....................... 10ms 硬盘顺序读取1M数据 ............... 20ms 从加拿大发送数据到荷兰再返回 ........ 150ms 看表得知,当我们往磁盘写日志的时候吞吐量受到磁盘本身速度的限制,铁定是没有写内存来得快的。但是内存呢又比硬盘贵,所以如果为了效率把数据全网内存里怼,那多少是有点壕。Mmap作为一种将磁盘上文件映射到内存中的技术,可以给我们更高效读写数据的能力。 往项目里添加依赖 在crate.io 里有两个库,一个是mmap,一个是mmap2,因为mmap这个库不怎么活跃了,所以这里我们用mmap2. 在Cargo.toml中添加 [dependencies] memmap2 = "0.9.4" 然后通过 use std::fs::OpenOptions; use memmap2::MmapMut; use std::path::PathBuf; let path = PathBuf::from("./map_file"); let file = OpenOptions::new().read(true).write(true).create(true).open(path)? let mmap = unsafe { MmapMut::map_mut(&file)? }; 这样mmap就是一个映射到磁盘上文件./map_file 的大byte数组,read是必须打开的,不然运行会报权限错误。 mmap在使用的时候,必须固定文件的大小,也就是说需要先固定内存的区域,然后再写数据 let path = PathBuf::from("./map_file"); let file = OpenOptions::new().read(true).write(true).append(true).create(true).open(path)? let mmap = unsafe { MmapMut::map_mut(&file)? }; mmap.set_len(1024); //这里设置了1K的大小 (&mut mmp[0..4).write_all(&[1,2,3,4])?; mmp.flush_async_range(0, 4)?; 这里我们只需要注意计算好索引位置,就能往指定的区块写入数据了。要注意的是,如果不执行flush,数据是不会回写到磁盘上的,这里有四种回写模式,前两种是带async和不带的,不带的是同步回写,也就是函数执行完后回确保数据已经回写到磁盘上了。带async是异步回写,也就是执行后会立即返回,只是确保把数据丢进了操作系统的IO队列,至于什么时候写成功,是操作系统控制的。另外两种是带range和不带的,不带range的回写是每一次执行都把内存文件全部覆写到磁盘文件里,如果文件很大就会占用很多的IO,带Range的是指定区域覆写,每次回写的IO有限,可以节约大量资源。建议用带Range的。 最后我们把读和写分别放到两个线程里。 thread::spwan(||{ let path = PathBuf::from("./map_file"); let file = OpenOptions::new().read(true).write(true).append(true).create(true).open(path)? let mmap = unsafe { MmapMut::map_mut(&file)? }; mmap.set_len(40); //这里设置了大小 for i in 0..10 { (&mut mmp[i*4..i*4+4).write_all(&[1,2,3,4])?; thread::sleep(Duration::from_sec(1)); } mmp.flush_async_range(i*4, 4)?; }); // 写线程 thread::spwan(||{ let path = PathBuf::from("./map_file"); let file = OpenOptions::new().read(true).write(true).append(true).create(true).open(path)? let mmap = unsafe { MmapMut::map_mut(&file)? }; mmap.set_len(40); //这里设置了大小 loop { println!("{:?}", mmap.as_ref()) thread::sleep(Duration::from_sec(1)); } }); 执行后的效果,首先每秒会打印出内存映射文件的内容,这里两个文件分别在两个不同线程里,但是文件是同一个,所以映射出来的mmap在两个线程里其实都是指向同一个内存块,所以看起来没联系,但是当写线程的内存块改变后,读线程里的内容获取出来也会变更。 因为这里写入的其实是内存,所以速度会非常的快,吞吐量会非常的大,如果你的系统需要走大量的IO,又想能持久化,又想快要走内存读写的话,好好的参悟mmap的使用,会非常的有帮助。 这里介绍了mmap的基本用法,下一篇,我们会借鉴Kafka的存储机制,来看看如何使用mmap来实现一个快速的日志持久化系统,

本文介绍了使用Rust的Mmap技术提高数据读写效率的方法,通过将文件映射到内存中实现快速读写,详细介绍了添加依赖、创建映射、固定内存区域、写入数据和回写到磁盘的步骤,最后实现了并发读写。使用Mmap技术可提高大量IO操作且需要持久化数据的系统性能。

Mmap Rust 并发读写 性能提升 数据读写

相关推荐 去reddit讨论

热榜 Top10

Dify.AI
Dify.AI
eolink
eolink
LigaAI
LigaAI
观测云
观测云

推荐或自荐