全局变量意味着更快的代码?

Bee*_*and 42 c c++ optimization

我最近在一篇关于 1996年编写的游戏编程文章中读到,使用全局变量比传递参数更快.

这是真的,如果是这样,今天仍然如此吗?

Ste*_*end 43

简短的回答 - 不,优秀的程序员通过了解和使用适当的工具来使代码更快,然后以有条不紊的方式优化他们的代码不符合他们的要求.

更长的答案 - 这篇文章,在我看来并不是特别精心编写的,在任何情况下都不是关于程序加速的一般建议,而是'15种做更快的blits的方法'.无论你如何看待文章的优点,将其推断为一般情况都是缺少作者的观点.

如果我正在寻找性能建议,我会在一篇没有识别或显示单个具体代码更改的文章中放置零信任以支持示例代码中的断言,并且不建议测量代码可能是个好主意.如果您不打算如何更好地编写代码,为什么要包含它?

一些建议已经过时 - 很久以前,FAR指针在PC上停止了问题.

一个认真的游戏开发者(或任何其他专业程序员,就此而言)会对这样的建议大笑:

您可以完全取出断言,也可以#define NDEBUG在编译最终版本时添加 .

我的建议,如果你真的希望评估这15个技巧中的任何一个的优点,并且因为这篇文章已经14年了,那就是用现代编译器编译代码(Visual C++ 10说)并尝试识别任何使用全局变量(或任何其他提示)的区域会使其更快.

[只是在开玩笑 - 我真正的建议是完全忽略这篇文章,并在你无法解决的工作中遇到问题时询问Stack Overflow的具体性能问题.这样,您获得的答案将经过同行评审,并得到示例代码或良好的外部证据以及当前的支持.

  • 您有一些奇怪的假设,即当今的每台计算机和编译器都是“现代的”。由于非常小的堆栈大小和代码空间,通常需要在嵌入式系统上使用全局变量。在速度方面,它在尝试实时处理数据时也有帮助(这是大多数嵌入式的目标)。 (2认同)
  • @Nick T,好吧,也许我应该在我的问题中提到这一点,我正在为 Android 智能手机开发。 (2认同)

Pet*_* G. 27

当您从参数切换到全局变量时,可能会发生以下三种情况之一:

  • 它运行得更快
  • 它运行相同
  • 它运行得慢一些

您将不得不测量性能,以便在非平凡的具体情况下看到更快的速度.这在1996年是真的,今天是真的,明天也是如此.

暂时搁置性能,大型项目中的全局变量引入了依赖关系,这几乎总是使维护和测试变得更加困难.

出于性能原因试图找到全局变量的合法用法时,我非常同意Preet的答案中例子:微控制器程序或设备驱动程序中经常需要的变量.极端情况是处理器寄存器,专门用于全局变量.

在推理全局变量与参数传递的性能时,编译器实现它们的方式是相关的.全局变量通常存储在固定位置.有时,编译器会生成直接寻址以访问全局变量.但有时,编译器会再使用一个间接,并为globals使用一种符号表.IIRC gcc for AIX在15年前做到了这一点.在这种环境中,小类型的全局变量总是慢于本地和参数传递.

另一方面,编译器可以通过将参数传递到堆栈中来传递参数,方法是将它们传递给寄存器或两者的混合.

  • +1任何问题"将*X*更快"只有一个有用的答案:*measure*. (12认同)
  • 哇.这怎么会被投票?这完全没用****."巴黎下雨了吗?" - "好吧,不管是下雨,还是不下雨,或者下雨了一点.如果你必须知道,前往巴黎.离开巴黎一会儿,我更喜欢阳光明媚的天气." (6认同)
  • @Konrad显然,我认为它非常有用.它有助于在调整性能时建立必要的思维模式.你能提出一些建设性的批评吗? (6认同)
  • (续)如果不是缓存局部性,参数传递确实总是低于全局性,性能方面.这是一个重要的观察,并不需要*任何*基准.将缓存局部性考虑在内会使这更加有趣,而且*现在*我们可能已经达到了一个仔细的基准测试会很有趣的地步.但是如何写出这样的基准呢?要注意哪些变量?我真的不满意几乎所有与性能相关的答案,只是简单地叫"衡量".他们只是躲闪子弹.提供基准.然后你有一个答案. (4认同)
  • 我想补充说,开关对维护是危险的. (3认同)
  • @Konrad:我必须同意@Peter G.在简单化的问题中没有公开过多的变量,比如函数有多少个参数,你正在使用的调用约定是什么,具体的平台是什么代码运行...默认情况下,一个以5个整数作为参数的函数将使用ia32中的堆栈,而它将在x86_64中的寄存器中传递它们.如上所述,唯一明智的答案是**措施** (2认同)

Cra*_*rks 22

每个人都已经给出了适当的警告答案,关于这是平台和程序特定的,需要实际测量时间等等.所以,所有已经说过,让我直接回答你的问题,针对x86和PowerPC上的游戏编程的具体情况.

在1996年,在某些情况下,将参数推送到堆栈上需要额外的指令,并且可能导致Intel CPU管道内的短暂停顿.在这些情况下,完全避免参数传递和从文字地址读取数据可能会有非常小的加速.

在大多数游戏机上使用的x86或PowerPC上都不再如此.使用全局变量通常比传递参数,原因有两个:

  • 参数传递现在更好地实现了.现代CPU在寄存器中传递其参数,因此从函数的参数列表中读取值比内存加载操作更快.x86使用寄存器阴影和存储转发,所以看起来像将数据混洗到堆栈并返回实际上可以是一个简单的寄存器移动.
  • 在大多数性能考虑因素中,数据缓存延迟远远超过CPU时钟速度.大量使用的堆栈几乎总是在缓存中.从任意全局地址加载可能导致高速缓存未命中,这是一个巨大的损失,因为内存控制器必须从主RAM获取数据.("巨大"这里是600次或更多次.)

  • +1用于在使用全局变量时将缓存局部性识别为主要罪魁祸首. (4认同)

pmg*_*pmg 12

你是什​​么意思,"更快"?

我知道一个事实,用全局变量理解一个程序比没有一个程序花费了我更多的时间.

如果程序员花费的额外时间少于用户在使用全局变量运行程序时获得的时间,那么我会说使用global更快.

但考虑到该计划将由10人每天一次运行2年.而且没有全局变量需要2.84632秒而全局变量需要2.84217秒(增加0.00415秒).这比TOTAL运行时少727秒.在程序员时间方面,获得10分钟的运行时间并不值得引入全局.

  • 这大多是真的.例如,如果您正在编写游戏,您只需0.02秒处理输入,勾选游戏时钟,进行碰撞检测,计算场景内容,构建场景图并将其渲染到屏幕上,你可以加快速度!正如其他人所说的那样,测量并不会不必要地进行微观优化. (3认同)
  • +1特别是如果全局变量意味着引入3个小时来解决一个问题,否则可能会在10分钟内解决. (2认同)

Pre*_*gha 11

在某种程度上,任何避免处理器指令(即更短的代码)的代码都会更快.但是要快多少?不是特别的!另请注意,编译器优化策略可能会导致代码更小.

如今,这只是对非常特定的应用程序的优化,通常在超时间关键驱动程序或微控制代码中.

  • 不对.更长的代码也可以更快.特别是在这种情况下,全局变量通常会导致高速缓存未命中,而非全局数据可以避免这种情况,全局变量*的速度非常低*. (4认同)

wro*_*ans 7

撇开可维护性和正确性的问题,基本上有两个因素可以控制全局数据与参数的性能.

当你创建全局时,你会避免复制.那稍快一些.当您按值传递参数时,必须将其复制,以便函数可以在其本地副本上工作,而不会损坏调用者的数据副本.至少在理论上.如果一些现代优化器确定您的代码表现良好,那么它们会做很棘手的事情.函数可能会自动内联,编译器可能会注意到函数对参数没有任何作用,只是优化掉任何复制.

当你创建一个全局时,你就是在缓存中.如果您的所有变量都包含在您的函数和一些参数中,那么数据将集中在一个地方.一些变量将在寄存器中,一些变量可能会立即存在于缓存中,因为它们彼此正确"相邻".使用大量全局变量基本上是缓存的病态行为.无法保证相同功能将使用各种全局变量.位置与使用没有明显的相关性.也许你有一个足够小的工作集,它在任何地方都没有任何区别,而且它们都会在缓存中结束.

所有这些只是加上我上面的海报所提出的观点:

当您从参数切换到全局变量时,可能会发生以下三种情况之一:

* it runs faster
* it runs the same
* it runs slower
Run Code Online (Sandbox Code Playgroud)

您将不得不测量性能,以便在非平凡的具体情况下看到更快的速度.这在1996年是真的,今天是真的,明天也是如此.

根据您的确切编译器的具体行为以及用于运行代码的硬件的精确详细信息,在某些情况下,全局变量可能会获得非常轻微的性能提升.这种可能性值得在一些运行速度太慢的代码上尝试.可能不值得投入,因为明天你的实验答案可能会改变.因此,正确的答案几乎总是采用"正确"的设计模式,避免更丑陋的设计.在故意尝试破坏您的项目之前,寻找更好的算法,更高效的数据结构等.从长远来看,收益会好得多.

而且,除了开发时间与用户时间参数之外,我将添加开发时间与摩尔时间参数.如果你认为摩尔定律每年会使计算机的速度再次降低一半,那么为了简单的整数,我们可以假设每周进展稳定1%.如果你正在寻找可以改善1%这样的事情的微优化,并且它会使项目因复杂化而增加一周,那么只需休息一周就会对用户的平均运行时间产生同样的影响.