【问题标题】:Allocating a large multidimensional vector causes memory "overshoot"分配大的多维向量会导致内存“过冲”
【发布时间】:2021-10-21 07:52:54
【问题描述】:

我在使用大型多维 std::vector 变量时观察到意外行为。特别是,当我分配一个大尺寸的 4 维向量时,使用内存分析器可以看出,我的任务很快就会使用比变量所需更多的内存。

重现此行为的 MWE 如下:

#include <vector>
#include <iostream>

int main(int argc, char *argv[]){

    getchar();
    std::vector<std::vector<std::vector<std::vector<int64_t>>>> F(1, std::vector<std::vector<std::vector<int64_t>>>(15, std::vector<std::vector<int64_t>>(256, std::vector<int64_t>(100000, 0))));
    getchar();
}

下图显示了任务使用的内存。阵列的大小大约需要 3GB,但内存使用量很快就会超过 6GB。请注意,向量的最后一个维度是最大的,这意味着向量类的内存开销相对较小。

这已在 64 位系统上的 Manjaro 下观察到,使用 g++(gcc 版本 11.1.0 (GCC))编译。我用psrecord记录内存使用情况。有趣的是,我无法为低维向量重现相同的行为。即使我只删除第一个维度(大小为 1),我也无法在我的系统上重现此过冲。

有谁知道这是从哪里来的,或者是否可以避免(不使用大的一维向量)?这样做的主要问题是我的操作接近系统提供的最大内存,这意味着短暂的过冲会导致内存交换。

我会很高兴有任何想法和 cmets。提前致谢。

【问题讨论】:

  • 这种调试问题比软件工程更适合Stack Overflow。提示:std::vector::vector(size_t n, const T&amp; value) 获取值的引用并将其复制到每个元素中。外部构造函数称为vector(1, 3gb_data_structure)。由于制作了副本,因此 2 倍的预期内存使用量听起来是正确的。
  • 向量的向量的向量真的是最好的数据结构吗?将至少一些维度放入一维缓冲区是相当常见的,并使用一些自定义索引来提供多维索引。
  • @amon 感谢您的 cmets!我会在以后的帖子中记住关于适用性的评论,谢谢!这听起来确实很有道理。我假设有某种向量或编译器优化可以避免这种复制更小的尺寸,否则我不明白为什么这只发生在 4 维的情况下。
  • std::vector&lt;std::vector&lt;std::vector&lt;int64_t&gt;&gt;&gt;(1, std::vector&lt;std::vector&lt;int64_t&gt;&gt;(256, std::vector&lt;int64_t&gt;(100000, 0))) 使对象大小为 3GB。然后将其复制到 F,因此 F 的大小也是 3GB,总共消耗 6GB。该行结束后,临时对象被销毁,然后您降回到仅消耗 3GB 的空间。恕我直言,解决方案是使用一维向量。这并不意味着您不能将其包装在一个伪造它具有多个维度的类中,如下所示:stackoverflow.com/a/43358434/4342498
  • @Yksisarvinen 是的。 vector 不能在这里使用&amp;&amp;,因为它会将值的N 复制到向量中。如果它移动了,您只能制作 1 个对象,而不是 N

标签: c++ memory


【解决方案1】:

这可能是由不同的原因引起的,例如 returning 没有使用智能指针(这可能会导致复制)。

但在您的特定情况下,您将某些内容传递给 F 变量的构造函数(无论如何),这会占用额外的内存直到构造函数返回。

哦!另一个可能的原因是,(非)智能向量实现应该保留超过所需的部分(以防万一,以加快以后的追加速度)。

示例:

以下是解决 OP 问题的内容:

#include <vector>
#include <iostream>

int main(int argc, char *argv[]){
    getchar();

    std::vector<std::vector<std::vector<std::vector<int64_t>>>> F(
        1, std::vector<std::vector<std::vector<int64_t>>>(15, std::vector<std::vector<int64_t>>(256))
    );
    for (int i = 0; i < 15; i++)
        for (int j = 0; j < 256; j++)
            F[0][i][j] = std::vector<int64_t>(100000, 0);

    getchar();
}

【讨论】:

  • 感谢您的回答。事实证明,就我而言,这确实是构造函数问题,但我同意其他原因也可能适用于其他情况。
【解决方案2】:

除了解释问题根源的@amon、@NathanOliver 和@Top-Master 的cmets,我将提供一个解决问题的代码示例。

#include <vector>
#include <iostream>

int main(int argc, char *argv[]){

    getchar();
    std::vector<std::vector<std::vector<std::vector<int64_t>>>> F(1, std::vector<std::vector<std::vector<int64_t>>>(15, std::vector<std::vector<int64_t>>(256)));
    for (int i=0; i<15; i++) for (int j=0; j<256; j++) F[0][i][j] = std::vector<int64_t>(100000, 0);
    getchar();
}

【讨论】:

  • 你可以编辑我的答案并添加上面的代码(而不是新的答案),但我会为你做这件事(几乎没有格式)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
相关资源
最近更新 更多