gcc / C ++:如果CPU负载很低,那么代码优化几乎没用,是吗?

时间:2016-01-08 12:16:22

标签: c++ performance gcc optimization compiler-optimization

我的同事喜欢使用gcc' -g -O0'用于构建生产二进制文件,因为如果发生核心转储,调试很容易。他说没有必要使用编译器优化或调整代码,因为他发现生产过程没有高CPU负载,例如30%左右。

我告诉他背后的原因,他告诉我:如果CPU负载不高,瓶颈一定不是我们的代码性能,应该是一些IO(磁盘/网络)。因此,通过使用gcc -O2来改善延迟和吞吐量毫无用处。这也表明我们在代码中没有太多改进,因为CPU不是瓶颈。这是对的吗?

4 个答案:

答案 0 :(得分:4)

关于CPU使用率〜优化

我希望程序中的大多数优化问题与高于平常的CPU负载相关联,因为我们说次优程序 比理论上需要的更多。但这里的“通常”是一个复杂的词。我不认为您可以选择系统范围的CPU负载百分比的硬值,优化变得有用。

如果我的程序在循环中重新分配char缓冲区,当它不需要时,我的程序运行速度可能比它需要慢十倍,而我的CPU使用率可能比它需要的十倍高并且,优化功能可能会使应用程序性能提高十倍......但CPU使用率可能仍然只是整个系统容量的0.5%。

即使我们要选择一个CPU负载阈值来开始分析和优化,在通用服务器上我会说30%太高了。但这取决于系统,因为如果你为只运行你的程序的嵌入式设备编程,并且因为它有足够的能力运行你的程序而被选择和购买,那么30%可能相对较低事物的方案。

此外,所有优化问题确实与高于平常的CPU负载有关。也许你只是等待sleep超过实际需要的时间,导致消息延迟增加,但实际上减少 CPU使用率。

tl;博士:您的同事的观点过于简单化,可能无法以任何有用的方式与现实相提并论。

关于构建优化级别

然而,关于问题的真正症结,部署发布版本并关闭所有编译器优化是相当不寻常的。编译器旨在在-O0发出非常幼稚的代码,并在-O1-O2进行2016年非常“标准”的优化。您通常希望将这些用于生产用途,否则您将浪费现代编译器的大部分功能。

许多人还倾向于不在发布版本中使用-g,因此部署的二进制文件更小,更易于客户处理。你可以通过这样做将一个45MB的可执行文件丢弃到1MB,这不会改变口袋。

这是否会使调试更加困难?是的,它可以。通常,如果找到错误,您希望接收再现步骤,然后您可以在应用程序的调试友好版本中重复这些步骤,并分析由此产生的堆栈跟踪。

但如果错误无法按需复制,或者只能在发布版本中复制,那么您可能会遇到问题。因此,对(-O1)保持基本优化似乎是合理的,但也要在(-g)中保留调试符号;优化本身不应该大大妨碍您分析客户提供的核心转储的能力,调试符号将允许您将信息与源代码相关联。

话虽这么说,你可以拥有你的蛋糕并吃掉它:

  • 使用-O2 -g
  • 构建您的应用程序
  • 复制生成的二进制文件
  • 在其中一个副本上执行strip,删除调试符号;否则二进制文件将完全相同
  • 永远存放它们
  • 部署已剥离的版本
  • 如果要分析核心转储,请根据原始的非剥离版本进行调试

您还应该在应用程序中有足够的日志记录,以便能够在不需要任何此类错误的情况下追踪大多数错误。

答案 1 :(得分:2)

在某些情况下,他可能是正确的,而且在其他情况下大多是不正确的(在某些情况下,他完全正确)。

如果你假设你运行1秒,CPU将忙碌0.3秒并等待0.7秒。如果您对代码进行了优化并且说得到了100%的改进,那么CPU将完成0.15秒内0.3秒的完成,并使任务在0.85秒内完成,而不是1秒(假设等待其他内容将花费相同的时间)。 / p>

但是,如果您遇到多核情况,CPU负载有时会被定义为正在使用的处理能力。因此,如果一个核心以100%运行而两个空闲,则CPU负载将变为33%,因此在这种情况下,30%的CPU负载可能是由于程序只能使用一个核心。在这种情况下,如果代码得到优化,它可以大大提高性能。

请注意,有时被认为是优化的实际上是一种悲观 - 这就是为什么衡量它的重要性。我已经看到了一些"优化"降低性能。有时优化会改变行为(特别是当你"改进源代码时),所以你应该确保它不会通过适当的测试来破坏任何东西。在进行性能测量之后,您应该决定是否值得以可调速性交换速度。

答案 2 :(得分:1)

可能的改进可能是使用最近的GCC使用gcc -Og -g进行编译。 -Og优化是调试器友好的。

此外,您可以使用gcc -O1 -g进行编译;你得到了许多(简单)优化,因此性能通常是-O2的90%(当然有一些例外情况,即使-O3很重要)。 core转储通常是可调试的。

这实际上取决于软件的类型和所需的可靠性以及调试的简便性。数字代码(HPC)与小型数据库后处理完全不同。

最后,使用-g3代替-g可能有所帮助(例如gcc -Wall -O1 -g3

BTW同步问题和死锁可能更有可能出现在优化代码上而非非优化代码上。

答案 3 :(得分:1)

这很简单:CPU时间不是免费的。我们喜欢认为它是,但它显然是错误的。在某些情况下,有各种放大效果可以使每个循环计数。

假设您开发了一个可在数百万移动设备上运行的应用。您的代码浪费每秒都会在4核设备上使用1到2年的连续设备。即使CPU利用率为0%,墙上时间延迟也会花费你的背光时间,而且不容忽视:背光使用大约30%的设备功率。

假设您开发了一个在数据中心运行的应用程序。您正在使用的核心的每10%是其他人不会使用的。在一天结束时,您在服务器上只有这么多内核,并且该服务器具有电源,冷却,维护和摊销成本。每1%的CPU使用量都有很容易确定的成本,而且它们不是零!

另一方面:开发者的时间并不是免费的,开发人员每一秒的注意力都需要相应的能量和资源投入才能使她或他活着,吃饱,快乐。然而,在这种情况下,开发人员需要做的就是翻转编译器开关。我个人不买“更容易调试”的神话。现代调试信息足以表达捕获寄存器使用,价值活力,代码复制等。优化并没有像15年前那样妨碍它。

如果您的企业有一个未充分利用的服务器,那么开发人员正在做的事情实际上可能没问题。但我在这里看到的确实是不愿意学习如何使用调试工具或适当的工具开始。