我编写了以下并行测试用例,它的运行速度至少比md5sum二进制文件慢2倍:
// dependencies are md-5 = "0.9.1", rayon = "1.5.0", hex-slice = "0.1.4"
use std::{
convert::TryInto,
env::args_os,
fs::File,
io::{Error, Read},
path::PathBuf,
};
use hex_slice::AsHex;
use md5::{self, digest::Digest};
use rayon::prelude::*;
fn main() {
args_os()
.skip(1)
.collect::<Vec<_>>()
.par_iter()
.for_each(|path| {
println!(
"{:02x} {}",
md5sum(PathBuf::from(&path)).unwrap().plain_hex(false),
path.to_string_lossy()
);
});
}
fn md5sum(path: PathBuf) -> Result<[u8; 16], Error> {
let mut file = File::open(path)?;
let mut ctx = md5::Md5::default();
let mut buf = vec![0u8; 2_097_152];
loop {
let bytes_read = file.read(&mut buf)?;
if bytes_read == 0 {
break;
}
ctx.update(&buf[0..bytes_read]);
}
Ok(ctx.finalize().try_into().unwrap())
}如果我去掉了并行性(将par_iter()改为iter()),它就会比md5sum二进制文件快一点,所以编译器和md-5crate肯定不是问题。该算法并不完全受I/O限制,因为两个md5sum进程比一个进程在两个文件上操作的速度要快一些。
测试背景:通过使用SysInternals RamMap清空工作集和备用列表,我在两次测试之间刷新了磁盘缓存。其他背景:我在WSL2中运行代码,但在Windows文件夹中对文件进行校验和。数据存储在传统的硬盘上。
我尝试了ThreadPool、使用tokio的异步读取(使用信号量的parallel+concurrent以确保没有打开太多文件)和Rayon (如上所述)。我尝试了从512KB到10MB的缓冲区大小。操作系统或驱动器控制器应该足够智能,可以尽可能快地读取数据,但它不能正常工作,可能是因为系统想要设置延迟的下限。有没有办法在并发读取的情况下获得不错的性能?
发布于 2021-05-11 18:21:20
不是一个完整的解决方案:我通过使用Semaphore限制I/O (不是互斥,因为在固态硬盘上我希望允许多次读取)获得了一些加速。我重新安排了代码,将读取与计算分开,但这不适合非常大的文件。
读取将是顺序的,但计算可以并行进行:
fn md5sum(io_lock: Arc<Semaphore>, path: PathBuf) -> Result<[u8; 16], Error> {
let buf = {
let _lock = io_lock.access();
let mut file = File::open(path)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
buf
};
Ok(md5::Md5::digest(&buf).try_into().unwrap())
}如果能知道是否有一种方法可以让操作系统优化读取,那就更好了。
https://stackoverflow.com/questions/67484826
复制相似问题