【问题标题】:Memory usage of large 2D static array and vector of vectors大型 2D 静态数组和向量的向量的内存使用情况
【发布时间】:2018-06-11 23:42:55
【问题描述】:

我需要为机器学习项目使用大矩阵 20000 * 20000。当它被实现为静态数组时,它使用大约 1.57 GB 的内存。如果它是用向量向量实现的,那么它使用的内存比静态数组(大约 3.06 GB)要多得多。我无法弄清楚背后的原因。

数组版本:

static double distanceMatrix[20000][20000] = {0};

向量的向量:

vector<vector<double>> distanceMatrix(20000, vector<double> (20000));

我用它们来存储点之间的距离。

for (int i = 0; i < 20000; i++){
    for (int j = i+1; j < 20000; j++)
        distanceMatrix[i][j] = euclid_dist(pointVec[i], pointVec[j]);
}

我还观察到,当我使用数组版本时,内存使用量在嵌套循环期间会逐渐增加。但是,在使用向量的向量时,内存使用量达到 3.06 GB,然后嵌套循环开始。

我使用 Xcode 调试导航器和活动监视器检查了内存使用情况。提前致谢!

【问题讨论】:

  • 延迟分配内存,直到它实际用于静态情况下?无论如何,20k*20k*8 已经超过 3GB
  • sizeof(double[20000][20000])3,200,000,000
  • 不要将向量的向量用于二维矩阵。使用大小为 n*m 的向量。使用operator()(int row, int column) 编写一个简单的包装类来访问元素。更好:搜索现有的。
  • 谢谢大家的回答。感谢您的建议@manni66。听起来很有效率。我会考虑的。

标签: c++ arrays xcode memory vector


【解决方案1】:

那是因为vector 的内存分配策略,当达到它的极限时可能是newsize=oldsize*const(实现依赖),另见vector memory allocation strategy

【讨论】:

  • 确实,内存分配策略可以发挥重要作用,尤其是当向量动态增长时(即reserve() 或不可以产生很大的性能差异)。但是,这里的向量都被预先初始化为固定数量的项目。通过执行 OP 来测量容量表明正好分配了 2000 个元素。对 OP 的观察具有误导性,因为它们只考虑了进程使用的物理内存,而不考虑用于将数据存储在向量或数组中的完整虚拟内存。
【解决方案2】:

首先,该阵列不占用 1.57 GB 内存。所以测量有问题。

用静态数组做实验

在 Xcode 中运行以下代码时,您会发现数组的大小正好是 3,2 Gb:

const size_t matsize=20000;
static double mat2D[matsize][matsize] = {0};  
cout<<"Double:             " << sizeof (double) <<endl;
cout<<"Array:              " << sizeof mat2D <<endl;
cout<<"                    " << sizeof(double)*matsize*matsize<<endl;
// ... then your loop

当程序启动时,它报告的内存消耗在进入循环之前只有 5,3MB,尽管静态数组已经存在。循环完成后,报告的内存消耗为 1,57 Gb,正如您所解释的。但仍然不是我们预期的 3,2Gb。

您读取的内存消耗数字是您的进程使用的物理内存。进程的剩余内存在虚拟内存中,虚拟内存要大得多(在我的实验中为 7 Gb)。

向量实验

首先,让我们看一下向量的大概内存大小,知道每个向量都有一个固定大小加上一个动态分配的可变大小(基于容量可以等于或大于实际存储在向量)。下面的代码可以给你一些估计:

vector<vector<double>> mat2D(matsize, vector<double> (matsize));
cout<<"Vector (fixed part):" << sizeof (vector<double>)<<endl;
cout<<"Vector capacity:   " << mat2D[0].capacity()<<endl;
cout<<"       (variable):  " << sizeof(double)*mat2D[0].capacity()<<endl;
cout<<"       (total):     " << sizeof(double)*mat2D[0].capacity() + sizeof(mat2D[0])<<endl;
cout<<"Vector of vector:   " << (sizeof(double)*mat2D[0].capacity() + sizeof(mat2D[0]))*matsize+sizeof(mat2D)<<endl;
// then your loop 

运行此代码将显示存储向量所需的内存约为 3,2 Gb + 480 Kb 开销(每个向量 24 字节 * 2001 个向量)。

在进入循环之前,我们会注意到已经使用了 3 Gb 的物理内存。因此,与阵列版本相比,此动态版本的 MacOS 使用了两倍的物理内存。这当然是因为分配过程需要真正预先访问更多内存:2000 行中的每一行都需要单独的分配和初始化。

结论

两种方法中使用的整体虚拟内存没有太大区别。我可以用 Xcode 在调试模式下测量大约 7Gb。由于向量的额外 480Kb 开销,向量变体使用的比以前多一点。

macOS 使用更多或更少物理内存的策略很复杂,可能取决于许多因素(包括访问模式),目标是在可用物理内存和交换成本之间找到最佳折衷。

但是物理内存使用量并不能代表进程的内存消耗。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-23
    • 1970-01-01
    • 2020-09-13
    • 2016-08-15
    • 1970-01-01
    • 2011-10-24
    相关资源
    最近更新 更多