Memory 当第二维度的大部分为空时,可空向量的内存效率最高的数组是什么?
我有一个很大的固定大小数组,其中包含Memory 当第二维度的大部分为空时,可空向量的内存效率最高的数组是什么?,memory,rust,Memory,Rust,我有一个很大的固定大小数组,其中包含u32的可变大小数组。大多数二维数组将为空(即,第一个数组将稀疏填充)。我认为Vec是最适合这两个维度的类型(Vec)。因为我的第一个数组可能相当大,所以我想找到最节省空间的方法来表示它 我看到两种选择: 我可以使用Vec。我猜想,由于选项是一个标记的并集,这将导致每个单元格被sizeof(Vec)四舍五入到标记的下一个单词边界 我可以直接为所有单元格使用Vec::with_capacity(0)。空的Vec在使用前是否分配零堆 哪个是最节省空间的方法?实际上
u32
的可变大小数组。大多数二维数组将为空(即,第一个数组将稀疏填充)。我认为Vec
是最适合这两个维度的类型(Vec
)。因为我的第一个数组可能相当大,所以我想找到最节省空间的方法来表示它
我看到两种选择:
Vec
。我猜想,由于选项
是一个标记的并集,这将导致每个单元格被sizeof(Vec)
四舍五入到标记的下一个单词边界Vec::with_capacity(0)
。空的Vec
在使用前是否分配零堆Vec
和Vec
具有相同的空间效率
,因此编译器足够聪明,可以识别在选项
的情况下,它可以通过在指针字段中输入0来表示无
。包含更多信息
指针指向的后备存储器呢?使用Vec::new
或Vec::with_capacity(0)
创建时(与第一个链接相同);在这种情况下,它使用一个特殊的、非空的“空指针”Vec
仅在推送某个内容或以其他方式强制其分配时才在堆上分配空间。因此,用于Vec
本身及其备份存储的空间是相同的。Vec
是一个不错的起点。每个条目花费3个指针,即使它是空的,并且对于已填充的条目,每个分配开销可能会增加。但是,根据您愿意做出的权衡,可能会有更好的解决方案
这将条目的大小从3个指针减少到2个指针。缺点是更改框中元素的数量既不方便(转换为和转换为Vec
),也更昂贵(重新分配)Vec
如果外部集合足够稀疏,这将节省大量内存。缺点是更高的访问成本(散列、扫描)和更高的每元素内存开销HashMap
- 如果集合只填充一次,并且您从未调整内部集合的大小,则可以使用拆分数据结构:
这不仅将每个条目的大小减少到1个指针,还消除了每个分配的开销
struct Nested<T> { data: Vec<T>, indices: Vec<usize>,// points after the last element of the i-th slice } impl<T> Nested<T> { fn get_range(&self, i: usize) -> std::ops::Range<usize> { assert!(i < self.indices.len()); if i > 0 { self.indices[i-1]..self.indices[i] } else { 0..self.indices[i] } } pub fn get(&self, i:usize) -> &[T] { let range = self.get_range(i); &self.data[range] } pub fn get_mut(&mut self, i:usize) -> &mut [T] { let range = self.get_range(i); &mut self.data[range] } }
为了节省更多内存,您可以将索引减少到struct嵌套{ 资料来源:Vec, 索引:Vec,//第i个切片最后一个元素后的点 } 嵌套的{ fn获取范围(&self,i:usize)->std::ops::range{ 断言!(i
0{ 自索引[i-1]…自索引[i] }否则{ 0.自索引[i] } } pub fn get(&self,i:usize)->和[T]{ 让范围=自我。获取范围(i); &self.data[范围] } pub fn get_mut(&mut self,i:usize)->&mut[T]{ 让范围=自我。获取范围(i); &多个自身数据[范围] } }
,将每个集合的元素数限制在40亿个u32
BTreeMap
可能比HashMap
更好,尤其是在需要按顺序遍历的情况下。