我正在Rust中开发一个性能关键型网络服务。对我的服务的请求看起来像一个数字ID的向量ids: Vec<u64>
。对于ids
中的每个id
,我的服务必须从连续存储在SSD上的一长串记录中读取第id
条记录。因为所有记录的大小RECORD_SIZE
相同(实际上大约为6 KB),则每条记录的位置都是完全可预测的,因此简单的解决方案简化为
for id in ids {
file.seek(SeekFrom::Start(id * RECORD_SIZE)).unwrap();
let mut record = vec![0u8; RECORD_SIZE];
file.read_exact(&mut record).unwrap();
records.push(record);
}
// Do something with `records`
现在,可悲的是,以下情况适用:
ids
的元素是不连续的、不可预测的、非结构化的,并且有效地等效于在[0, N]
范围内均匀随机分布。N
太大了,我无法将整个文件存储在内存中。ids.len()
比N
小得多,因此如果不将99%的读取用于与ids
无关的记录,我就无法高效地线性循环文件。
现在,阅读规格,我的SSD的原始QD 32 IOPS应该允许我及时收集所有记录(即,在下一个请求到来之前)。但是,我观察到我的琐碎实现要糟糕得多。我怀疑这是由于它实际上是一个QD 1实现:
- 从磁盘的随机位置读取某些内容。
- 等待数据到达,将其存储在RAM中。
- 从磁盘的另一个独立位置读取下一个内容。
现在,问题是我一开始就知道所有的ids
,如果有一种方法可以指定,我会很高兴:
- 尽可能并行地读取与
ids
的每个元素相关的所有位置。 - 当这件事完成后,继续做每件事。
我想知道在Rust中是否有一种简单的方法来完成这一点。我在标准库中寻找类似file.parallel_read
的函数,在www.example.com上寻找有用的板条crates.io,但都无济于事。这让我很困惑,因为这应该是服务器/数据库设置中相对常见的问题。我错过了什么吗?
1条答案
按热度按时间amrnrhlw1#
根据您的目标体系结构,
posix_fadvise
系统调用如下:程序可以使用
posix_fadvise()
来声明将来要以特定模式访问文件数据的意图,从而允许内核执行适当的优化。你可以传递偏移量
RECORD_SIZE
,可能还有POSIX_FADV_WILLNEED
通知。函数和常量都在libc机箱中可用。同样的想法可以通过使用posix_madvise()
和POSIX_MADV_WILLNEED
的内存Map文件来实现,正如注解中所暗示的那样。然后你需要做一些性能调整来决定提前多少时间调用这些函数。如果不够早,数据就不会在你需要的时候出现,太早意味着你不必要地增加了系统内存的压力。