全局变量的C++性能表现

15

澄清一下:我知道全局变量的危害以及何时不应使用它们 :)

  • 在编译后的C++程序中,访问/设置全局变量与本地变量相比是否会有性能惩罚?

5
更好的问题应该是“这有什么关系?”当有人开始微调这样的细节时,这告诉我他们有性能问题并正在寻找解决方法。如果是这种情况,进行剖析,找出瓶颈所在。不要为这种事情担心。这不值得你浪费时间。 - greyfade
1
无论如何,我认为我终于找到了一种不需要太多麻烦就能提高我的声誉的方法:只需在问题中加入“性能”这个词,突然间你就会得到很多投票,让你不知道该怎么办! - Matthieu M.
1
“我知道全局变量的危害,以及何时不应使用它们”:永远没有使用全局变量的好理由,只有懒惰。你唯一需要的全局变量是常量。 - Matthieu M.
9个回答

21

这完全取决于您的机器架构。全局变量通过单个已知地址访问,而局部变量通常通过索引地址寄存器进行访问。两者之间的差异显著的可能性非常小,但如果您认为这很重要,您应该为目标架构编写一个测试并测量差异。


为什么这么多东西都取决于我的机器架构 :( 这使得优化变得困难。 - Dr. Acula
1
如果你想要优化你的程序,你应该对其进行性能分析并找到瓶颈,而不是猜测可能存在的瓶颈。除非你以一种完全破坏缓存的方式频繁访问全局变量,否则你几乎肯定不会看到与此相关的任何瓶颈。 - Andrew Khosravian
4
电脑是现实世界的机器,而非理论概念。如果你需要让你的代码尽可能地快速运行,你就必须解决现实世界机器的各种怪癖。当然,99.99%的情况下你并不需要让你的代码以最快的速度运行,只需要足够快即可,这时你就不应该考虑优化,而应该交由编译器自动处理。 - anon
1
延迟优化可以让您花更多时间关注关键理念:正确性和健壮性。只有在客户投诉(包括内部客户)时才花时间优化性能。市场团队可能希望增加更多功能,而不是提高程序的性能。 - Thomas Matthews
在紧密的循环中,您可以使用本地变量获得缓存优势,堆栈通常会很热。当然,这是假设有一个大而复杂的处理器,DSP、小型嵌入式芯片等则不会有此问题... - Hayman
1
实际上,本地变量极有可能不会通过寄存器被访问,而是将被存储在寄存器中。这意味着它们会快得多(速度能提高10倍)。 - MSalters

10

这要看情况,但通常是可以的,尽管这是一个微小的问题。全局变量应该可以从许多上下文中引用,这意味着将它们放入寄存器中是不可能的。而在本地变量的情况下,这是可能且更可取的。事实上,范围越窄,编译器就有更多机会优化对该变量的访问/修改。


2
实际上,在gcc中,你可以将全局变量放置在一个寄存器中:register unsigned long *ds asm("ebx"); 但是由于这样会导致整个代码库的寄存器资源不足,所以可能会更慢。 - geocar

9
本地变量在许多情况下可能会更“快”,但我认为性能增益不会很明显,而且维护许多全局变量的成本会超过这种优势。我列出的所有内容要么成本微不足道,要么可以轻松被程序中的其他任何低效所淹没。我认为这些是微观优化的完美例子。
本地变量存储在堆栈上,堆栈更有可能在高速缓存中。如果您的全局变量经常使用,则此点无意义,因为它也将在高速缓存中。
本地变量的作用域仅限于函数-因此,编译器可以假定它们不会被任何其他函数调用更改。对于全局变量,编译器可能被迫重新加载全局值。
在某些64位计算机上,获取全局变量的地址是一个两步过程-您还必须将全局的32位偏移量添加到64位基址中。本地变量始终可以直接从堆栈指针访问。

5

严格来说,不是的。

需要考虑以下几点: 全局变量会增加程序在内存中的静态大小。 如果访问变量需要同步,那么这将导致一些性能开销。


5

局部变量可以进行许多编译器优化,但是全局变量则不能。因此,在某些情况下,您可能会看到性能上的差异。不过,我怀疑您的全局变量是否在性能关键循环中被访问(如果是,则设计非常糟糕!),所以这可能不是一个问题。


3
答案与程序的整体结构有关。
例如,我刚刚对此进行了反汇编,在两种情况下,循环变量都被移动到寄存器中,之后没有任何区别。
int n = 9;
int main()
{
    for (n = 0; n < 10; ++n)
        printf("%d", n);

    for (int r = 0; r < 10; ++r)
        printf("%d", r);

    return 0;
}

为了确保,我尝试使用类来做类似的事情,但没有看到任何区别。但是如果全局变量在不同的编译单元中,可能会有所改变。


3
更重要的是如何使用存储在变量中的数据,而不是如何声明它们,这对性能有影响。我不确定这里的术语是否正确,但可以定义两种数据访问方式:共享访问(从代码的不同部分访问相同的数据)和私有数据,每个部分都有自己的数据。默认情况下,全局变量意味着共享访问,而局部变量意味着私有访问。但是,两种类型的变量都可以实现两种类型的访问(例如,局部指针指向相同的内存块,或全局数组,其中代码的每个部分访问数组的不同部分)。
共享访问具有更好的缓存、更低的内存占用,但在多线程环境中很难优化。特别是在NUMA架构下,它也不利于扩展。
私有访问更易于优化,更适合扩展。私有访问的问题通常存在于具有多个相同数据副本的情况下。这些情况通常与更高的内存占用、副本之间的同步、较差的缓存等问题有关。

1

你不需要担心任何性能惩罚。除了其他人所说的之外,你还应该考虑分页开销。局部实例变量是从对象结构中获取的,该结构很可能已经被分页到缓存内存中。另一方面,全局变量可能会导致不同模式的虚拟内存分页。

再次强调,这些性能问题真的不值得你考虑。


1
除了其他回答之外,我想要指出,在多线程环境中访问全局变量很可能会更加昂贵,因为你需要确保它被正确地锁定,并且线程可能会排队等待访问它。而对于局部变量来说,这并不是一个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接