在C++中分配大内存块

Spe*_*ath 6 c++ memory-management

我试图在浮点值的C++中为3D矩阵分配一个大的内存块.它的尺寸是44100x2200x2.这应该采用44100x2200x2x4字节的内存,大约7.7gb.我正在使用带有Ubuntu的64位x86机器上的g ++编译我的代码.当我使用htop查看进程时,我发现内存使用量增长到32gb并被迅速杀死.我在记忆计算中犯了错误吗?

这是我的代码:

#include <iostream>

using namespace std;
int main(int argc, char* argv[]) {
  int N = 22000;
  int M = 44100;
  float*** a = new float**[N];
  for (int m = 0; m<N; m+=1) {
    cout<<((float)m/(float)N)<<endl;
    a[m] = new float*[M - 1];
    for (int n = 0; n<M - 1; n+=1) {
      a[m][n] = new float[2];
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我的计算不正确,我的分配接近38gb.我现在修复了代码以分配15gb.

#include <iostream>

using namespace std;
int main(int argc, char* argv[]) {
  unsigned long  N = 22000;
  unsigned long  M = 44100;
  unsigned long blk_dim = N*(M-1)*2;
  float* blk = new float[blk_dim];
  unsigned long b = (unsigned long) blk;

  float*** a = new float**[N];
  for (int m = 0; m<N; m+=1) {
    unsigned long offset1 = m*(M - 1)*2*sizeof(float);
    a[m] = new float*[M - 1];
    for (int n = 0; n<M - 1; n+=1) {
      unsigned long offset2 = n*2*sizeof(float);
      a[m][n] = (float*)(offset1 + offset2 + b);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Sam*_*hik 11

你忘记了一个维度,以及分配内存的开销.所示代码在第三维中非常低效地分配存储器,导致过多的开销.

float*** a = new float**[N];
Run Code Online (Sandbox Code Playgroud)

这将大致分配,22000 * sizeof(float **)这是一个鲁棒的176kb.可以忽略不计.

a[m] = new float*[M - 1];
Run Code Online (Sandbox Code Playgroud)

这里的单个分配将是44099 * sizeof(float *),但你将获得22000这些.22000 * 44099 * sizeof(float *),或大约7.7gb的额外内存.这是您停止计数的地方,但您的代码尚未完成.它还有很长的路要走.

a[m][n] = new float[2];
Run Code Online (Sandbox Code Playgroud)

这是8个字节的单个分配,但这个分配将完成22000*44099次.这是另一个 7.7gb冲了下来.你现在已经超过了15个应用程序所需的内存,大致需要分配.

每次分配不是免费的,而且new float[2]需要超过8个字节.每个单独分配的块必须由C++库在内部跟踪,以便可以通过它进行回收delete.堆分配的最简单的基于链接列表的实现需要一个前向指针,一个后向指针,以及分配的块中有多少字节的计数.假设在对齐目的时不需要填充任何内容,在64位平台上,每个分配至少需要24字节的开销.

现在,因为你的第三个维度为22000*44099分配,为第二个维度分配22000个分配,为第一个维度分配一个:如果我指望我的手指,这将需要(22000*44099 + 22000 + 1)*24,或者另外22千兆字节的内存,只是为了消耗最简单的基本内存分配方案的开销.

如果我的数学运算正确的话,我们现在使用最简单,可能的堆分配跟踪需要大约38千兆字节的RAM.您的C++实现可能使用稍微复杂的堆分配逻辑,但开销较大.

摆脱了new float[2].计算矩阵的大小和new一个7.7gb的块,然后计算指针的其余部分应该指向的位置.另外,为矩阵的第二维分配一块内存,并计算第一维的指针.

您的分配代码应该只执行三个new语句.一个用于第一个维度指针,一个用于第二个维度指针.还有一个包含第三维的大量数据.

  • 在这种情况下,你可以使用`float*bigArray = new float [M*N*O];`并编写getter和setter方法来计算一维数组中的正确偏移量.最重要的是,由于数组是常规的,你只需要对`new`进行一次调用. (4认同)
  • 对8字节块使用那么多开销的任何分配方案都是垃圾.合理的分配方案每个分配最多使用8个字节,而良好的分配方案每个页面仅使用一个专用于特定大小块的小块,因此8字节分配只需1-2%的开销. (2认同)
  • @SpentDeath 我的意思是您正在为大量单元格分配空间。如果实际上不需要其中的大多数(否则它们无论如何都会携带一些默认值)*并且*您不需要将这个怪物的部分或全部提供给一些需要连续内存的API(您发布的代码在-无论如何都没有问题),您可以使用其他选项。例如,将 int 映射到 int 到 2 元数组的映射。性能不会那么好(显然,在最好的情况下,我们正在谈论通过无序映射进行散列),但内存利用率可能会*明显*好得多。 (2认同)