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语句.一个用于第一个维度指针,一个用于第二个维度指针.还有一个包含第三维的大量数据.