2个版本
0.1.1 | 2023年4月20日 |
---|---|
0.1.0 | 2023年4月19日 |
#382 在 内存管理
27 每月下载
14KB
244 代码行
DiskAlloc
对于非常大的向量:在磁盘上分配。
动机
如果您有非常大的向量,您可能希望将它们保存在磁盘上而不是内存中。
似乎没有绕过序列化和反序列化的方法,但使用这个crate,您可以直接在磁盘上分配“内存”!
它是如何工作的?
Linux提供文件的内存映射。这意味着您可以有一个1TB大小的文件,将其映射到用户空间的地址,并像实际拥有1TB内存一样使用它。
操作系统会根据需要交换页面,并处理所有这些。
向量通常加倍和减半大小。
这个crate首先在内存中映射了一个0B文件的512GiB。
如果您的向量需要调整大小,DiskAlloc会根据您的需求增长或缩小文件。映射不一定保证是可增长的,这就是为什么它必须从一开始就非常大。
陷阱
进行I/O操作可能会失败。
因此,如果您想确保向量的调整大小不会使整个程序崩溃,则应使用 Vec::try_reserve()
。
此外,请注意在堆上存储数据的结构。
Vec<String>
保留所有内容在堆上,只会存储指向堆上字符串的指针在磁盘上,这在任何情况下都没有意义。
请参阅 SmallString 了解如何尽可能将字符串保留在向量本身中。
如果您有可序列化和反序列化的项目,并且可以处理有限的向量功能,请参阅 swapvec。
对于每个向量,请使用一个 DiskAlloc
实例。
一旦在单个文件上使用多个向量,所有优化都会消失。
也不要一次性创建过多的 DiskAlloc
实例。
每个映射都需要512GiB的地址范围,所以创建过多会导致 OutOfMemory
错误。
注意
如果您在 htop
中跟踪您的应用程序,您将看到,htop 显示您的进程内存使用量很高。
这包括当前保存在RAM中的数据,但它仍然被计算为文件缓冲区(参见RAM条的黄色部分)。
使用方法
简单且最安全
#![feature(allocator_api)]
use diskallocator::DiskAlloc;
fn main() {
let alloc = DiskAlloc::new().unwrap();
let mut v: Vec<u32, DiskAlloc> = Vec::new_in(alloc);
for i in 0..100 {
v.push(i);
}
}
高级
#![feature(allocator_api)]
use std::{fs::OpenOptions, thread, time::Duration};
use diskallocator::DiskAlloc;
// Size 4 bytes * 16 = 64B
struct Dummy {
_txt: [char; 16],
}
impl Dummy {
pub fn new() -> Self {
Self {
_txt: [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', '1', '2', '3', '4', '5', '6', '7', '8',
],
}
}
}
fn main() {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("dummy.file")
.unwrap();
let alloc = DiskAlloc::on_file(file).unwrap();
let mut v: Vec<Dummy, DiskAlloc> = Vec::new_in(alloc);
let giga = 1;
let items_per_kb = 16;
for _ in 0..giga * items_per_kb * 1024 * 1024 {
v.push(Dummy::new());
}
println!("Check file dummy.file!");
// thread::sleep(Duration::from_secs(999999));
}
依赖关系
~1.6–9.5MB
~105K SLoC