匿名命名空间中的全局变量优化

Global variable optimisation in anonymous namespace

这是一个例子:

namespace {
    int a = 0;
}

int main() {
    a = 1;
}

GCC and Clang optimized a = 1 out, but MSVC 没有。

GCC 的输出示例:

main:
        xor     eax, eax
        ret

另一个例子:

namespace {
    int a = 0;
}

int main() {
    a++;
}

None 个编译器优化了 a++GCC, Clang, MSVC.

GCC 的输出示例:

main:
        add     DWORD PTR _ZN12_GLOBAL__N_11aE[rip], 1
        xor     eax, eax
        ret

是否有一些推理,为什么编译器优化了一些全局变量,但不优化其他变量?针对特定的编译器做出回答是可以的。

对于您的第一个示例,如果您使用 /GL 标志,MSVC 还将优化全局(并写入)。不幸的是,当你这样做时,它不会产生汇编语言输出,所以 Godbolt 只会告诉你输出文件丢失了。要检查代码,您必须生成一个实际的可执行文件,然后反汇编它,以获得:

0000000140001000: 33 C0              xor         eax,eax
0000000140001002: C3                 ret

...但即使 /GL 也不会优化第二种情况的增量:

0000000140001000: FF 05 AA 6B 01 00  inc         dword ptr [0000000140017BB0h]
0000000140001006: 33 C0              xor         eax,eax
0000000140001008: C3                 ret

“为什么”很难回答,只是因为它归结为从事编译器优化的人们的动机。然而,我猜想他们主要是响应(至少是感知到的)客户的需求。因此,对于 gcc 来说,很多事情都归结为特定优化是否会显着帮助(或损害)Linux 等流行开源包的性能。同样,对于 MSVC,它主要是关于流行的软件包,如 Windows 和 Office(不,我并不是要暗示他们也忽略了他们的外部客户)。

因此,在这种情况下,您的代码在程序启动时产生的影响很小。那么最大的问题是,这是否会转化为对其他足够普遍的代码产生更大的影响,以至于他们有动力对其进行优化。我猜想至少到目前为止,他们还没有看到很多出现这种情况的案例。考虑到大多数人创建和初始化全局变量并从那时起忽略它的频率,我的猜测是他们根本没有太多理由在这种特殊情况下工作。它的优化程度仅限于它足够相似,可以受到他们主要为其他情况编写的优化的影响。例如,如果您在这个一般顺序上添加另一个修改 a

namespace {
int a = 0;
}

void foo()
{
    for (int i=0;i<10;i++)
        a++;
}

int main()
{
    a++;
    foo();
}

...编译器将单独的增量操作变成单个赋值可能并不奇怪:

0000000140001000: C7 05 A6 6B 01 00  mov         dword ptr [0000000140017BB0h],0Bh
                  0B 00 00 00
000000014000100A: 33 C0              xor         eax,eax
000000014000100C: C3                 ret

但这只是应用基本的强度降低,这是几十年来众所周知的标准优化之一,大多数编译器几乎都做了这么长时间。我想你可能会争辩说消除全局变量只是死代码消除的一个应用,但是作为一个全局变量使它成为一种特殊情况,所以即使他们通常明确地进行死代码消除,它也没有得到在这种情况下(始终如一地)应用。