[英]Memory size grows exorbitantly with dynamic 2D memory allocation
我有以下一段示例代碼分配1D數組:
#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
。
現在,如果我使用以下代碼實現動態2D數組:
#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
,消耗的內存為86 GB。 這不符合我能想到的任何數學。 任何人都可以幫我解決這個問題嗎?
你的2D數組實際上是一個指向3 int
數組的指針數組。 所需的額外空間來自3 int
所有小數組中的開銷:每個數組使用12個字節加上可能的4到12個字節的填充和估計的至少8個字節的開銷。 總大小可達32GB + 8GB = 40GB,據top
報道為38GiB。 根據malloc
的實際實現,開銷可以從更少到更多。 malloc
返回的malloc
保證適當地對齊以滿足最大對齊要求。 在intel 64位架構上,這意味着16個字節。 如果分配器非常保守,則每個小數組占用16個字節,否則可能占用32個字節或更多。
您可以通過這種方式分配一個真正的2D數組,而無需開銷:
#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
以GiB顯示內存大小,單位為1024x1024x1024字節,比GB小約8%。
指針數組恰好使用8GB(80億字節),開銷可以忽略不計。
下表總結了指針數組與使用malloc
分配的大小為C
的各個int
數組之間的細分:
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字節的最小塊大小
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.