【问题标题】:Multidimensional Arrays Memory Usage in C (Heap)C(堆)中的多维数组内存使用情况
【发布时间】:2015-12-08 22:08:07
【问题描述】:

我在理解以下代码的内存使用方面有问题:

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]。
任务管理器显示内存使用量为 560MB。二氧化碳
如果我尝试自己计算内存使用量,我将无法理解该值。

这是我的计算(对于 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

【问题讨论】:

  • 你的代码中没有“多维数组”。
  • 不要将malloc和朋友的结果投射到C中!注意:成为三星级程序员并不是恭维。
  • 可能是malloc 开销让您感到困扰。您的代码对malloc() 进行了 2²²×3 次调用,如果您分配一块内存并自己拆分它会更好。
  • @FUZxxl 你能举个例子吗,怎么做?
  • @Memphisd 给我一分钟。

标签: c memory-management multidimensional-array heap-memory


【解决方案1】:

4194304 不是 2^20,它更像是 2^22,因此您的计算至少相差了 4 倍。而且您还分配了一组指针来指向其他数据,这会占用空间。在您的代码中,第一个 malloc 分配 2048*2048 指针不是指向那么多项目的单个指针。

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

1) 不要强制执行 malloc 返回

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]);

【讨论】:

  • 哦,好吧,你是对的。但是,如果我用 2^22 计算它是 320MB。你指的是哪个“指向其他数据的指针”?为什么它们要占用 160MB 的内存?
  • @Memphisd 我的意思是第一个 malloc。它为 400 万个指针分配存储空间,然后在 i 循环中再分配 400 万个指针来初始化它们。我认为你把这一切都搞反了。您应该分配几个 (2*3) 包含 400 万个项目的大型数组。循环应由内向外翻转。
【解决方案2】:

您的代码执行 2²²·4 + 1 = 16777217 次调用 malloc()。对于每个分配的内存区域,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;
    }
}

这里我们只调用了 3 次 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,与观察到的内存量非常匹配。

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

【讨论】:

  • 刚刚偶然发现这篇文章,我认为它必须是:thirds + amount_reg * j + amount_reg * dim * i;在最里面的循环中
  • @Memphisd 感谢您纠正此问题。多么愚蠢的错误。
猜你喜欢
  • 2020-12-19
  • 2011-11-30
  • 2018-12-29
  • 1970-01-01
  • 2010-10-19
  • 1970-01-01
  • 2013-08-13
  • 2016-05-20
  • 2010-09-25
相关资源
最近更新 更多