2016-03-14 74 views
3

如願生長我有以下片的示例代碼分配1D陣列:內存大小與動態2D存儲器分配

#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的數據在存儲器中。請注意0​​和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=7C=8存儲器消耗的是54 GB,而C=16消耗的內存是86 GB,這個d不適合任何我能想出來的數學。任何人都可以幫我解決這個問題嗎?

+6

塊大小'malloc'(大多數分配器)給8首或24個字節**至少**給出尺寸。 – Jarod42

+0

首先,這似乎不是C++代碼。其次,你如何衡量使用過的內存? – SergeyA

+0

'int **'不是一個二維數組,它甚至不能指向這樣一個數組。 – Olaf

回答

4

你的二維數組實際上是一個指向數組的指針數組,它的數量爲3 int。所需的額外空間來自3 int的所有小陣列的開銷:每個都使用12個字節加上4到12個字節的可能填充以及至少8個字節的估計開銷。總大小可能達到32GB + 8GB = 40GB,這是由top報告的38GiB。取決於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在吉布顯示內存大小,單位1024x1024 1024個字節,比GB小約8%。

指針數組正好使用8GB(80億字節),開銷可以忽略不計。

下表總結的指針數組和int單獨的陣列與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的情況下,看是否最小的塊大小是用於

+0

正如我寫的,我會假設塊對齊的開銷結果OS/malloc庫使用的任何邊界(字節邊界或機器字) –

+0

是的,我在Intel 64位體系結構上運行。我仍然無法弄清楚的是,如果將C的值增加到4,5,6,則所消耗的內存完全相同(38 GB),而對於C = 7和C = 8,消耗的內存爲54 GB,而對於C = 16,消耗的內存爲86 GB。如果我假設填充到16,32或64位,它仍然不符合數學。另外,我用0.1s刷新間隔監視了運行top命令所消耗的內存總量,因此大小可能不是非常精確(+或 - 1GB)。 – ambuj

+0

我更新了答案,並解釋了內存消耗 – chqrlie