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