【问题标题】:In C: Is it faster to access a char* = malloc() used like a 2D array than an array[][]?在 C 语言中:访问 char* = malloc() 像 2D 数组一样使用比访问数组 [] [] 更快吗?
【发布时间】:2015-06-23 18:08:08
【问题描述】:

偶然发现了这个最近的问题:

How can I have a dynamically allocated 2D array in C?

我只是想知道:当我使用简单的 malloc 创建一个二维数组并自己管理类似二维的访问时:

int row=100;
int col=100;
char* buffer = malloc(sizeof(char)*row*col);
for(int i=0;i<row;i++){
    for(int j=0;j<col;j++){
        buffer[i*col+j]=128;
     }
}

这会比创建“常规”二维数组时(显着)更快吗,因为在前者中我通过顺序访问实现缓冲区优化?还是我想错了?

int row=100;
int col=100;
char buffer[row][col];
for(int i=0;i<row;i++){
    for(int j=0;j<col;j++){
        buffer[i][j]=128;
     }
}  

感谢您的解释。

【问题讨论】:

  • 在第一种情况下,您可能会使用 int idx = i*col; 获得更快的访问速度; for(int j=0;j
  • 另外,索引应该是缓冲区[i*col+j]
  • 好的,感谢您的 cmets,尽管关于顺序访问的速度优势的一般问题仍然存在。
  • 嗯,这会给我一个价值,但没有解释我关于为什么它更快的理论是否正确......
  • 了解哪种理论正确的唯一方法是在您的系统上进行尝试。不同的条件可能会产生不同的结果。

标签: c arrays memory optimization


【解决方案1】:

撇开动态内存分配的(小)开销,如果您通过[row][column]* (row * rowsize + column) 访问内存区域中的特定元素,则没有区别。这基本上只是符号的不同。

所以您的问题更像是“将数组定义为“行优先”比“列优先”更好吗?

答案是:只有您自己知道,因为您是根据应用程序的需要定义对内存区域的访问模式的人。

除非您处理非常大的数组(其中一维大于适合您的缓存的数组),否则我不会考虑太多。

【讨论】:

  • 谢谢!我不会期望访问特定元素会有太大的不同,但我想知道在按顺序访问所有元素时是否会有(通常可以解释的)差异,就像我在嵌套循环中所做的那样。这个问题的意思更像是“在行和列中定义数组还是在一个多合一行中定义数组更好?”
  • @mfro 我不认为这是一个好的建议。由于我在帖子中提到的原因,使用 1D 几乎总是更好。您同意 1D 使用更好的缓存,但使用 1D 也有更多优势。
  • “行优先问题而不是列优先问题”归结为:我的机器将 sizeof(element) 添加到寄存器是否比添加 sizeof(element) + number of columns 更快(请注意,此添加减少为常数编译时间)。例如。对于 100x100 字符数组,它将是“* (x + 100)*(x + 1) 快吗?”。对于大多数情况(除非你有很多行/列,你总是可以构造一个矛盾的例子),我想没有太大区别。
  • 我认为我缺少的一点是:当我像示例2那样在堆栈上创建一个静态二维数组时,它是否仍会占用内存中的连续区域?如果是这样,那么我理解您为什么说差异将难以衡量
  • 哦,堆栈上的静态分配当然也为您提供了一个连续的内存区域
【解决方案2】:

注 1:

在第一个代码 sn-p 中,数组分配在进程的堆上,而在第二个 sn-p 中,您在堆栈上分配缓冲区。如果你想使用更大的数组,你可能会得到一个 ... stackoverflow :)

注2:

我的解释重点是您希望使用 Type**(在您的情况下为 int**)动态分配 2D 数组的情况。

当您处理二维数组时,将其分配为一维数组会更快并使用智能索引将其作为二维数组进行访问。这是因为:

  • 一维数组填充内存中的连续空间(降低碎片)。这样可以更好地缓存阵列,减少访问延迟。
  • 当你分配一个二维数组时,你有另一个级别的间接,这意味着你需要先获取行的地址,然后再访问元素。当您使用一维数组时,您可以直接访问元素。
  • 当数组以一维方式分配时,很容易将其与缓存行大小对齐。这将使编译器更容易优化事务并避免必须对位于缓存行边界上的元素进行 2 次读取。
  • 处理一维数组,应该有助于编译器生成更好的矢量化代码

【讨论】:

  • 请注意,第二个 sn-p 无论如何都不会在实际代码中工作,因为 row column 必须是编译时常量
  • 关于您的陈述:一维数组确实填充了连续空间。就像 2D 数组(由 OP 定义)一样。没有区别。 “碎片化”在这里不是问题。 1.)如果您free() 内存管理器无法有效重用/合并的任意大小的区域,我书中的碎片化意味着在堆中浪费内存。堆栈不能分片,这是对的,但一个单一的动态分配也不能。 2.) 如果你声明一个二维数组然后用一维数组“模拟”一个二维数组,那么间接没有区别,它总是归结为base address + offset
  • @mfro 我的回答更多是指使用 Type** 动态分配二维数组。在 OP 提出的情况下,您是对的。二维数组是在栈上分配的,所以碎片不是问题。
猜你喜欢
  • 1970-01-01
  • 2017-04-06
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-03
  • 2019-06-15
相关资源
最近更新 更多