【问题标题】:Why it is significantly slower to reference global variables in a for loop?为什么在 for 循环中引用全局变量要慢得多?
【发布时间】:2020-10-21 01:16:07
【问题描述】:

我正在读一本书,它说使用局部变量来消除不必要的内存引用。比如下面的代码效率不是很高:

int gsum;  //global sum variable
void foo(int num) {
    for (int i = 0; i < num; i++) {
       gsum += i;
    }
}

下面的代码效率更高:

void foo(int num) {
    int fsum;
    for (int i = 0; i < num; i++) {
       fsum += i;
    }
    gsum = fsum;
}

我知道第二种情况使用存储在寄存器中的局部变量。这就是为什么它要快一点,而在第一种情况下,gsum 必须从主内存中检索太多次。

但我还有疑问:

Q1- gcc 编译器是否不够聪明,无法检测到它并隐式使用寄存器来存储全局变量,以便后续引用将完全像第二种情况一样使用该寄存器?

Q2- 如果由于某种原因编译器无法优化,那么我们仍然有缓存。从缓存中引用全局变量仍然非常快,但我看到一些使用局部变量的程序比引用全局变量的程序快 10 倍。这是为什么呢?

【问题讨论】:

  • 查看what actually happens。生成的代码本质上是if (num &gt; 0) gsum += num * num;,根本没有循环。请注意,如果num 为负数,则行为未定义,因为必须发生整数溢出,因此编译器可以在这种情况下为所欲为(这里,什么都没有)。
  • 在那个例子中,gcc实际上是gsum += num * (num - 1) + num;,原因我不明白。铿锵不会那样做。可能只是错过了优化(或者可能是未错过的悲观化)。
  • 但是把它改成this 这样的东西,这不是很容易用封闭的形式表达的,你会看到编译器完全按照你的建议做。该值在整个循环中保存在一个寄存器中,并且只在最后存储回来。
  • 一个更棘手的情况是,如果您在循环中调用(非内联)函数。编译器无法知道函数是否访问全局变量,因此它必须在每次函数调用时存储并重新加载它。如果您知道这不会发生,那么使用局部变量可能会有所帮助。
  • 第二个版本使用一个未初始化的fsum,设置它为0gsum 全局变量保证为0 局部不是。

标签: c


【解决方案1】:

Q1:其他函数可能需要该寄存器,这些函数将在后续调用foo 之间调用。这意味着每当调用此函数时,gsum 都需要穿梭进出寄存器。

Q2:包含gsum 的页面可能会在缓存中停留一段时间。但是,根据您的计算机正在执行的其他操作,该页面可能会被写入交换空间,以便为其他页面腾出内存空间。

【讨论】:

    猜你喜欢
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-29
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    相关资源
    最近更新 更多