gpt4 book ai didi

c - 访问静态或动态分配的内存是否更快?

转载 作者:太空狗 更新时间:2023-10-29 16:32:03 25 4
gpt4 key购买 nike

在 C 中有两种分配全局数组的方法:

  • 静态地
    char data[65536];
  • 动态地
    char *data;

    data = (char*)malloc(65536); /* or whatever size */

  • 问题是,哪种方法性能更好?多少?

    据了解,第一种方法应该更快。

    因为使用第二种方法,要访问数组,每次访问时都必须取消引用元素的地址,如下所示:
  • 读取变量 data其中包含指向数组开头的指针
  • 计算到特定元素的偏移量
  • 访问元素

  • 使用第一种方法,编译器对 data 的地址进行硬编码。变量进入代码,第一步被跳过,所以我们有:
  • 计算从编译时定义的固定地址到特定元素的偏移量
  • 访问数组的元素

  • 每次内存访问大约相当于 40 个 CPU 时钟周期,因此,使用动态分配,特别是对于不频繁读取的情况,与静态分配相比,性能会显着下降,因为 data一些更频繁访问的变量可能会从缓存中清除变量。相反,解引用静态分配的全局变量的成本是 0,因为它的地址已经在代码中进行了硬编码。

    这样对吗?

    最佳答案

    人们应该始终进行基准测试以确保。但是,暂时忽略缓存的影响,效率可能取决于您访问两者的偶发性。这里,考虑 char data_s[65536]char *data_p = malloc(65536)
    如果访问是零星的,静态/全局会稍微快一点:

    // slower because we must fetch data_p and then store
    void
    datasetp(int idx,char val)
    {

    data_p[idx] = val;
    }

    // faster because we can store directly
    void
    datasets(int idx,char val)
    {

    data_s[idx] = val;
    }

    现在,如果我们考虑缓存, datasetpdatasets [第一次访问后] 将大致相同,因为获取 data_p将从缓存中实现[不保证,但可能],因此时间差要小得多。

    但是,当以紧密循环访问数据时,它们将大致相同,因为编译器 [优化器] 会预取 data_p在循环开始处并将其放入寄存器中:
    void
    datasetalls(char val)
    {
    int idx;

    for (idx = 0; idx < 65536; ++idx)
    data_s[idx] = val;
    }

    void
    datasetallp(char val)
    {
    int idx;

    for (idx = 0; idx < 65536; ++idx)
    data_p[idx] = val;
    }

    void
    datasetallp_optimized(char val)
    {
    int idx;
    register char *reg;

    // the optimizer will generate the equivalent code to this
    reg = data_p;

    for (idx = 0; idx < 65536; ++idx)
    reg[idx] = val;
    }

    如果访问如此零星以至于 data_p从缓存中被逐出,然后,性能差异并不那么重要,因为对 [任一] 数组的访问很少见。因此,不是代码调整的目标。

    如果发生这种驱逐,实际的数据数组很可能也会被驱逐。

    一个更大的数组可能有更多的影响(例如,如果我们有 65536 而不是 100000000 ,那么仅仅遍历就会驱逐 data_p 并且当我们到达数组的末尾时,最左边的条目已经被驱逐。

    但是,在这种情况下, data_p 的额外获取将是 0.000001% 的开销。

    因此,它有助于对特定用例/访问模式进行基准测试 [或建模]。

    更新:

    基于一些进一步的实验[由彼得的评论触发], datasetallp函数未优化为等效于 datasetallp_optimized对于某些条件,由于严格的混叠考虑。

    因为数组是 char [或 unsigned char ],编译器生成 data_p在每次循环迭代中获取。请注意,如果数组不是 char (例如 int ),优化确实发生并且 data_p只获取一次,因为 char除了 int 之外的任何别名都可以更有限。

    如果我们改变 char *data_pchar *restrict data_p我们得到了优化的行为。添加 restrict告诉编译器 data_p不会给任何东西 [甚至它自己] 取别名,所以优化提取是安全的。

    个人笔记:虽然我理解这一点,但对我来说,如果没有 restrict 似乎很愚蠢。 ,编译器必须假设 data_p可以别名回自身。尽管我确定还有其他 [同样人为的] 示例,但我唯一能想到的就是 data_p指向自身或那个 data_p是结构的一部分:
    // simplest
    char *data_p = malloc(65536);
    data_p = (void *) &data_p;

    // closer to real world
    struct mystruct {
    ...
    char *data_p;
    ...
    };
    struct mystruct mystruct;
    mystruct.data_p = (void *) &mystruct;

    这些将是获取优化错误的情况。但是,IMO,这些与我们一直在处理的简单案例是有区别的。至少,结构版本。而且,如果程序员应该做第一个,IMO,他们会得到他们应得的 [并且编译器应该允许 fetch 优化]。

    对于我自己,我总是手工编写相当于 datasetallp_optimized 的代码。 [无 register ],所以我通常不会过多地看到 multifetch “问题” [如果你愿意的话]。关于我的意图,我一直相信“给编译器一个有用的提示”,所以我只是公理地做这件事。它告诉编译器和另一个程序员意图是“只获取一次 data_p”。

    另外,使用 data_p时不会出现multifetch问题。对于输入 [因为我们没有修改任何东西,所以不考虑混叠]:
    // does fetch of data_p once at loop start
    int
    datasumallp(void)
    {
    int idx;
    int sum;

    sum = 0;
    for (idx = 0; idx < 65536; ++idx)
    sum += data_p[idx];

    return sum;
    }

    但是,虽然它可能相当普遍,但使用显式数组“硬连线”原始数组操作函数 [或者 data_sdata_p ] 通常不如将数组地址作为参数传递有用。

    旁注: clang将使用 data_s 优化一些功能进入 memset调用,因此,在实验过程中,我稍微修改了示例代码以防止出现这种情况。
    void
    dataincallx(array_t *data,int val)
    {
    int idx;

    for (idx = 0; idx < 65536; ++idx)
    data[idx] = val + idx;
    }

    这不会受到 multifetch 问题的影响。即, dataincallx(data_s,17)dataincallx(data_p,37)工作大致相同 [使用最初的额外 data_p拿来]。这更有可能是人们通常可能使用的[更好的代码重用等]。

    所以, data_s之间的区别和 data_p变得更有争议了。再加上明智的使用 restrict [或使用除 char 以外的类型], data_p fetch 开销可以最小化到它不是真正需要考虑的程度。

    现在更多地归结为选择固定大小数组或动态分配数组的架构/设计选择。其他人已经指出了权衡。

    这取决于用例。

    如果我们有有限数量的数组函数,但有大量不同的数组,那么将数组地址传递给函数显然是赢家。

    然而,如果我们有大量的数组操作函数和[说]一个数组(例如[2D]数组是一个游戏板或网格),那么每个函数引用全局[或 data_s]可能会更好。或 data_p ] 直接地。

    关于c - 访问静态或动态分配的内存是否更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34847662/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com