与堆栈变量相比,为什么访问全局静态变量可以提高性能?

arm*_*ues 4 c++ optimization performance stack static

我试图了解全局静态变量的性能,并遇到了一个非常奇怪的场景。下面的代码平均需要大约 525 毫秒。

static unsigned long long s_Data = 1;

int main()
{
    unsigned long long x = 0;

    for (int i = 0; i < 1'000'000'000; i++)
    {
        x += i + s_Data;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

下面的代码平均需要 1050 毫秒。

static unsigned long long s_Data = 1;

int main()
{
    unsigned long long x = 0;

    for (int i = 0; i < 1'000'000'000; i++)
    {
        x += i;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我知道访问静态变量很快,根据我的其他测试,写入它们很慢,但我不确定在上述场景中我错过了哪些信息。注意:编译器优化已关闭,并使用 MSVC 编译器来执行测试。

小智 8

为了解决实际问题,在关闭优化的情况下,我们可以转向生成的程序集来了解为什么一个程序集比另一个程序集运行得更快。

在第一个测试中,GCC(主干)https://godbolt.org/z/GdssT9vME生成此程序集

s_Data:
        .quad   1
main:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], 0
        mov     DWORD PTR [rbp-12], 0
        jmp     .L2
.L3:
        mov     eax, DWORD PTR [rbp-12]
        movsx   rdx, eax
        mov     rax, QWORD PTR s_Data[rip]
        add     rax, rdx
        add     QWORD PTR [rbp-8], rax
        add     DWORD PTR [rbp-12], 1
.L2:
        cmp     DWORD PTR [rbp-12], 999999999
        jle     .L3
        mov     eax, 0
        pop     rbp
        ret
Run Code Online (Sandbox Code Playgroud)

第二个测试https://godbolt.org/z/5ndnEv5Ts我们得到

main:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], 0
        mov     DWORD PTR [rbp-12], 0
        jmp     .L2
.L3:
        mov     eax, DWORD PTR [rbp-12]
        cdqe
        add     QWORD PTR [rbp-8], rax
        add     DWORD PTR [rbp-12], 1
.L2:
        cmp     DWORD PTR [rbp-12], 999999999
        jle     .L3
        mov     eax, 0
        pop     rbp
        ret
Run Code Online (Sandbox Code Playgroud)

比较这两个程序,第一个程序有十六条指令,而第二个程序只有十四条指令。(我相信你可以猜到不同的指令也有不同的CPU周期开销)
每条汇编指令需要多少个CPU周期?

正如我的评论中所指出的,优化极大地改变了生成的程序集。
两个测试都会产生这个-O2

main:
        xor     eax, eax
        ret
Run Code Online (Sandbox Code Playgroud)