C(堆)中的多维数组内存使用

Multidimensional Arrays Memory Usage in C (Heap)

我无法理解以下代码的内存使用情况:

typedef struct list{
    uint64_t*** entrys;
    int dimension;
    uint64_t len;
} list;

void init_list(list * t, uint64_t dim, uint64_t length, int amount_reg)
{
    t->dimension = dim;
    t->len=length;
    t->entrys = (uint64_t ***) malloc(sizeof(uint64_t**)*length);
    uint64_t i;
   for(i=0;i<length;i++)
   {
       t->entrys[i] = (uint64_t **) malloc(sizeof(uint64_t *)*dim); 
       int j;
       for(j=0;j<dim;j++)
       {
           t->entrys[i][j]=(uint64_t *) malloc(sizeof(uint64_t)*amount_reg);
       }
   }
}

int main()
{
     list * table = (list *) malloc(sizeof(list));
     init_list(table,3,2048*2048,2);
     _getch();
}

我想做的是分配 uint64_t 个元素的 3d 数组,例如 table[4194304][3][2].
taskmanager 显示内存使用量为 560MB。 cO
如果我尝试自己计算内存使用量,我无法理解该值。

这是我的计算(对于 x64 系统):

2^20 * 8 Byte (first dimension pointers)
+ 2^20 * 3 * 8 Byte (second dimension pointers)
+ 2^20 * 3 * 2 * 8 Byte (for the values itsself)

= 2^20 * 8 Byte * 10 = 80MB

也许我的计算完全错误,或者我的代码产生了大量开销?!

如果是这样,有没有办法让这个程序更有效地利用内存? 我无法想象像 ~2^23 uint64_t 这样的值需要这么多内存(因为 2^23*8Byte 只是 64MB

4194304 不是 2^20,它更像是 2^22,所以你的计算至少有 4 倍的偏差。而且你还分配了一组指针指向其他数据,这需要 space。在您的代码中,第一个 malloc 分配 2048*2048 指针不是指向那么多项目的单个指针。

您还应该使用动态分配的最佳实践:

1) 不要强制转换 malloc return

2) 始终使用 expression = malloc(count * sizeof *expression); 这样无论您在表达式中使用多少指针级别,您都不会弄错大小。例如

 t->entrys       = malloc(length     * sizeof *t->entrys);
 t->entrys[i]    = malloc(dim        * sizeof *t->entrys[i]);
 t->entrys[i][j] = malloc(amount_reg * sizeof *t->entrys[i][j]);

您的代码对 malloc() 进行了 2²² · 4 + 1 = 16777217 次调用。对于每个分配的内存区域,malloc() 都会做一些簿记。当您多次调用 malloc() 时,这会加起来。您可以像这样减少调用 malloc() 次来减少开销:

void init_list(list * t, int dim, uint64_t length, int amount_reg)
{
    uint64_t ***entries = malloc(sizeof *entries * length);
    uint64_t **seconds = malloc(sizeof *seconds * length * dim);
    uint64_t *thirds = malloc(sizeof *thirds * length * dim * amount_reg);

    uint64_t i, j;

    t->entrys = entries;

    for (i = 0; i < length; i++) {
        t->entrys[i] = seconds + dim * i;
        for (j = 0; j < dim; j++)
            t->entrys[i][j] = thirds + amount_reg * j + amount_reg * dim * i;
    }
}

这里我们只调用 malloc() 三次,内存使用量从 561272 KiB 下降到 332020 KiB。为什么内存使用率仍然很高?因为你计算有误。分配分配了这么多内存:

  • 条目:sizeof(uint64_t**) * length = 8 · 2²²
  • 秒:sizeof(uint64_t*) * length * dim = 8 · 2²² · 3
  • 三分之一:sizeof(uint64_t) * length * dim * amount_reg = 8 · 2²² · 3 · 2

我们总共有 (1 + 3 + 6) · 8 · 2²² = 335544320 字节(327680 KiB 或 320 MiB)的 RAM,这与观察到的内存量非常匹配。

如何进一步减少这个数额?考虑转置您的数组,以便轴按大小的升序排序。这样你在指针上浪费的内存就会少得多。您还可以考虑仅为值分配 space 并手动进行索引计算。这可以大大加快代码速度(减少内存访问)并节省内存,但编程起来很乏味。