我有以下一段代码分配一维数组:
#define C 3 int main() { int *a; long long N = 1000000000, i; a = (int*)malloc(sizeof(int) * N * C); for (i = 0; i < N * C; i++) a[i] = i / 2; printf("%d\n", a[N*C - 1]); return 0; }
上面的代码在内存中只需要12 GB的数据。
请注意sizeof(int) == 4
和sizeof(int*) == 8
。
现在,如果我使用以下代码实现dynamic二维数组:
#define C 3 int main() { int **a; long long N = 1000000000, i; a = (int**)malloc(sizeof(int*) * N); for (i = 0; i < N; i++) a[i] = (int*)malloc(sizeof(int) * C); for (i = 0; i < N; i++) for (j = 0; j < C; j++) a[i][j] = i; printf("%d\n", a[N-1][C-1]); return 0; }
上面的代码奇怪的需要大约38 GB的内存(而它应该采取12GB + 8GB(指针数组)= 20 GB。
奇怪的是,在第二个示例代码中,如果将C
的值增加到4,5,6,则消耗的内存是完全相同的(38 GB),而对于C=7
和C=8
,消耗的内存是54 GB ,而对于C=16
,消耗的内存是86GB。 这不适合我能弄清楚的任何math。 任何人都可以帮我解决这个问题吗?
你的2D数组实际上是一个指向3 int
数组指针的数组。 所需的额外空间来自3 int
所有小数组的开销:每个使用12个字节加上可能的4到12个字节的填充以及至少8个字节的估计开销。 总的尺寸可以达到32GB + 8GB = 40GB,这是上面报道的38GiB。 取决于malloc
的实际实现,开销可以从少一点到多得多。 malloc
返回的malloc
保证适合最大对齐要求。 在Intel 64位架构上,这意味着16个字节。 如果分配器非常保守,每个小数组占用16个字节,否则占用32个字节或更多。
你可以通过这种方式分配一个真正的二维数组
#define C 3 int main(void) { long long N = 1000000000, i; int (*a)[C] = malloc(sizeof(*a) * N); for (i = 0; i < N; i++) { for (j = 0; j < C; j++) a[i][j] = i; } printf("%d\n", a[N-1][C-1]); return 0; }
编辑试图解释你的大小观察:
C=3 to C=6 -> 38GiB C=7, C=8 -> 54 GiB C=16 -> 86 GiB
top
显示内存大小,以1024Byte为单位,比GB小约8%。
指针数组正好使用8GB(80亿字节),开销可以忽略不计。
下表总结了指针数组和用malloc
分配的大小为C
的单个数组之间的细分:
C used actual arrays pointers total binary --- ---- ------ ------ -------- ----- ------ 3 12 32 32GB 8GB 40GB 37.3GiB 4 16 32 32GB 8GB 40GB 37.3GiB 5 20 32 32GB 8GB 40GB 37,3GiB 6 24 32 32GB 8GB 40GB 37.3GiB 7 28 48 48GB 8GB 56GB 52.2GiB 8 32 48 48GB 8GB 56GB 52.2GiB 16 64 80 80GB 8GB 88GB 82.0GiB
我的解释是:
由malloc
分配的小内存块四舍五入为16加8的倍数,再加上额外的8字节开销,用于竞技场簿记信息。 malloc
返回的地址在16字节边界上对齐,8字节开销位于块前面,块大小是16减8字节的倍数,以允许下一个块被对齐。
这将解释C = 7从32跳转到48个字节。
你应该确认C = 11有类似的跳转。
您也可以测量C = 2的情况,以查看块大小为最小块大小是8还是24个字节