Performance HashMap和Vec之间的高效内存转换
我正在尝试将一个大的Performance HashMap和Vec之间的高效内存转换,performance,memory-management,collections,rust,out-of-memory,Performance,Memory Management,Collections,Rust,Out Of Memory,我正在尝试将一个大的HashMap转换为Vec。通常的做法如下所示: // initialize HashMap let cap = 50000000; let mut hm: HashMap<usize, usize> = HashMap::new(); for i in 0..cap { hm.insert(i, i); } // convert HashMap to Vec let vec = hm.into_iter().collect::<Vec<(us
HashMap
转换为Vec
。通常的做法如下所示:
// initialize HashMap
let cap = 50000000;
let mut hm: HashMap<usize, usize> = HashMap::new();
for i in 0..cap {
hm.insert(i, i);
}
// convert HashMap to Vec
let vec = hm.into_iter().collect::<Vec<(usize, usize)>>();
有没有更好(更有效或更惯用)的方法来解决这个问题?如果要提高性能,我愿意使用不安全的代码
编辑
正如所指出的,到iter
在迭代过程中不会解除分配,因此建议的解决方案不能按预期工作。除了将HashMap
转储到文件中,然后将该文件读入Vec
之外,还有其他方法可以转换这些集合吗?您似乎对Vec
的fromtiterator
特性的实现不满意。我不知道在std中更改它是否合理。但是,您可以为Vec
引入一个包装器,并根据自己的意愿实现fromtimiterator
:
#[derive(Debug)]
struct OptimizedVec<T>(Vec<T>);
impl<T> std::iter::FromIterator<T> for OptimizedVec<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> OptimizedVec<T> {
let mut vec = Vec::with_capacity(100);
for i in iter {
vec.push(i);
// reserve few megabytes
if vec.capacity() - vec.len() < 10 {
vec.reserve_exact(1000000);
}
}
OptimizedVec(vec)
}
}
//...
let vec: OptimizedVec<_> = hm.into_iter().collect();
#[派生(调试)]
结构优化DVEC(Vec);
impl std::iter::用于OptimizedVec的FromIterator{
#[内联]
fn来自国际热核实验堆(iter:I)->优化DVEC{
设mut-vec=vec::具有_容量(100);
国际热核聚变实验堆{
向量推(i);
//保留几兆字节
如果向量容量(){
向量储备(1000000);
}
}
优化DVEC(vec)
}
}
//...
让vec:OptimizedVec=hm.into_iter().collect();
Vec
值将作为Vec.0
访问,预先分配所需的准确数量是节省内存和时间的解决方案
假设您要创建一个包含100项的向量。如果要为50个项目分配空间,则在添加项目51时,存在两种可能性:
分配可以适当延长,你可以继续快乐的生活
分配无法在适当的位置进行扩展,因此需要进行新的、更大的分配。所有数据需要从上一次分配中复制;可能是O(n)操作。在此复制期间,两个分配都处于活动状态,占用50+100个插槽,比原始分配的大小适当时占用更多空间
不可能知道哪种情况会发生,所以你必须假设最坏的情况
这是Iterator
具有size\u hint
方法的原因之一:知道要分配多少项更有效
另一方面,HashMap
可能将数据存储在一个大的分配中,因为它更高效。这意味着不可能(或者可能不容易/有效)将一个项目移出,然后减少分配。即使您可以这样做,在拷贝开始时,您将同时分配整个HashMap
和Vec
我认为有两种可能性可以改善这种情况:
如果HashMap
将数据内部存储在Vec
中,则可能会在HashMap
中添加一个方法,该方法在最后一分钟清理后返回Vec
完全避免存储HashMap
和/或Vec
。例如,如果您需要对数据进行迭代,则不需要先收集到Vec
;只需重复它
您确定第二个代码的内存开销更小吗?我认为,IntoIter
迭代器不会在迭代过程中释放内存。实际上,用很少的额外内存进行对话并不容易……如果没有足够的内存同时存储HashMap
和Vec
,您可能需要切换计算机,或者重新构造程序,以便能够处理较小的工作块(例如MapReduce)。实际上,您的净空很小:如果问题大小增加了50%,您很可能只需要HashMap
,然后您将怎么办?除非我完全误解了某些内容,否则在std
中修复它肯定是不合理的。内存优化实现将比现在的实现方式慢得多。我也怀疑OPs自己的实现是否有帮助……我曾计划将代码包装到自定义结构中,但为了问题的简单性,我没有发布它。我理解不应该更改std
s的实现,因为这会带来巨大的时间影响。我的用例非常罕见,我想知道是否有比连续的reserve\u-exact
调用更好的方法。这个想法是你保留一个相对较小的项目块,这样push就不必重新分配。我将编辑这个问题以避免错误信息(我认为into_iter在遍历迭代器时会释放内存)。我想我记得HashMap
使用了3个向量,编码如下:(哈希、键、值)。因此,没有从HashMap
到Vec
的简单转换。
#[derive(Debug)]
struct OptimizedVec<T>(Vec<T>);
impl<T> std::iter::FromIterator<T> for OptimizedVec<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> OptimizedVec<T> {
let mut vec = Vec::with_capacity(100);
for i in iter {
vec.push(i);
// reserve few megabytes
if vec.capacity() - vec.len() < 10 {
vec.reserve_exact(1000000);
}
}
OptimizedVec(vec)
}
}
//...
let vec: OptimizedVec<_> = hm.into_iter().collect();