【问题标题】:What is the most memory-efficient array of nullable vectors when most of the second dimension will be empty?当大部分第二维为空时,最节省内存的可空向量数组是什么?
【发布时间】:2018-06-01 06:37:07
【问题描述】:

我有一个较大的固定大小数组u32 的可变大小数组。大多数第二维数组将为空(即第一个数组将稀疏填充)。我认为Vec 是最适合这两个维度的类型(Vec<Vec<u32>>)。因为我的第一个数组可能非常大,所以我想找到最节省空间的方式来表示它。

我看到两个选项:

  1. 我可以使用Vec<Option<Vec<u32>>>。我猜Option 是一个标记联合,这将导致每个单元格被sizeof(Vec<u32>) 向上舍入到标记的下一个单词边界。

  2. 我可以直接对所有单元格使用Vec::with_capacity(0)。一个空的Vec 分配零堆直到它被使用吗?

哪种方法最节省空间?

【问题讨论】:

  • 我猜... — 为什么不直接使用mem::size_of 来找出大小?
  • 谢谢!鉴于我对这门语言非常陌生,我仍然希望能将这些点连在一起。

标签: memory rust


【解决方案1】:

Vec<Vec<T>> 是一个不错的起点。每个条目需要 3 个指针,即使它是空的,并且对于已填充的条目,可能会有额外的每次分配开销。但根据您愿意做出的权衡,可能会有更好的解决方案。

  • Vec<Box<[T]>> 这将条目的大小从 3 个指针减少到 2 个指针。缺点是更改框中元素的数量既不方便(与Vec<T> 相互转换),而且成本更高(重新分配)。
  • HashMap<usize, Vec<T>> 如果外部集合足够稀疏,这会节省大量内存。缺点是更高的访问成本(散列、扫描)和更高的每元素内存开销。
  • 如果集合只填充一次并且您从不调整内部集合的大小,则可以使用拆分数据结构:

    这不仅将每个条目的大小减少到 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]
        }
    }
    

    为了额外节省内存,您可以将索引减少到 u32,将每个集合限制为 40 亿个元素。

【讨论】:

  • 利用稀疏的好主意!我特别喜欢第三种选择。对于第二个,我怀疑BTreeMap 可能比HashMap 更好,尤其是在需要按顺序遍历的情况下。
【解决方案2】:

其实Vec&lt;Vec&lt;T&gt;&gt;Vec&lt;Option&lt;Vec&lt;T&gt;&gt;&gt;的空间效率是一样的。

A Vec contains a pointer that will never be null,所以编译器足够聪明,可以识别在Option&lt;Vec&lt;T&gt;&gt;的情况下,它可以通过在指针字段中输入0来表示NoneWhat is the overhead of Rust's Option type? 包含更多信息。

指针指向的后备存储呢? A Vec doesn't allocate(与第一个链接相同)当您使用Vec::newVec::with_capacity(0) 创建它时;在这种情况下,它使用一个特殊的、非空的“空指针”。 Vec 仅在您 push 某事或以其他方式强制它分配时在堆上分配空间。因此,Vec 本身和其后备存储所使用的空间是相同的。

【讨论】:

    猜你喜欢
    • 2018-12-22
    • 2013-01-08
    • 2012-11-10
    • 1970-01-01
    • 2022-01-23
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多