C++ 优化

4

我正在处理一些现有的 C++ 代码,它似乎写得很差,并且被频繁调用。我在想是否应该花时间对其进行更改,或者编译器已经优化了这个问题。

我正在使用 Visual Studio 2008。

以下是一个示例:

void someDrawingFunction(....)
{
  GetContext().DrawSomething(...);
  GetContext().DrawSomething(...);
  GetContext().DrawSomething(...);
  .
  .
  .
}

这是我会做的方法:
void someDrawingFunction(....)
{
  MyContext &c = GetContext();
  c.DrawSomething(...);
  c.DrawSomething(...);
  c.DrawSomething(...);
  .
  .
  .
}

那是Visual Studio 2008吗? - epotter
最近我也不得不进行这种优化。在进行之前,我确实对代码进行了分析。 - Constantin
感谢epotter的指出。让我来将VS 9(2008)合并到2009中。 :-) - Tim Rupe
抽样会告诉我们GetContext()是否需要超过epsilon时间,如果时间是你所担心的。有些人可能会想知道他们的Humm-V的油漆涂层是否会使其获得更好的油耗。我猜你不是那种人。 - Mike Dunlavey
6个回答

25

不要猜测程序花费时间的地方。 首先进行性能剖析,找到瓶颈,然后优化它们。

至于GetContext(),这取决于它有多复杂。如果只返回类成员变量,则编译器很可能会内联它。如果GetContext()需要执行更复杂的操作(例如在表中查找上下文),则编译器可能不会内联它,您可能希望像第二个片段中那样仅调用一次。

如果您使用GCC,还可以为GetContext()函数打上pure属性。这将允许它执行更多的优化,例如公共子表达式消除


1
+1:个人资料。在进行任何更改之前,请编写一组单元测试。没有单元测试意味着您可能会轻易地破坏某些东西而不知道。 - S.Lott
特别是编译器需要在同一翻译单元中(例如通过头文件)看到GetContext()的源代码,以便能够内联它或者推断它是纯函数并且可以安全地优化掉。 - j_random_hacker
即使GetContext()没有内联,似乎这种“优化”也不太可能产生可测量的差异。你必须进行性能分析! - Mark Ransom
哪个版本的GCC允许您使用pure属性?这是C++0x的特性还是我弄错了? - Carson Myers
1
@Carons Myers:这是GCC的扩展功能,不是C++98或C++0x的特性。根据文档,它在GCC 2.96及更高版本中可用。 - Adam Rosenfield

11

如果您确定存在性能问题,请将其更改。如果 GetContext 是一个函数调用(而不是一个宏或内联函数),那么编译器每次都必须调用它,因为编译器不能确定它的作用,因此,编译器可能不知道可以消除该调用。

当然,您需要确保 GetContext 总是返回相同的内容,并且这种“优化”是安全的。


当然。不过请注意,在“DrawSomething”上下文中,所有的GetContext()可能都是相同的。 - dmckee --- ex-moderator kitten
在绘图函数内部,GetContext()将始终返回相同的对象引用(代码中的成员变量)。根据Adam的建议,我稍后会尝试一些分析,并查看是否对性能产生了影响。当在屏幕上绘制数十万个物体时,即使进行最微小的改进,也会让我很高兴。 - Tim Rupe
此外,如果 GetContext() 类似于变量的 getter,则其速度可能与本地变量一样快。如果它相当复杂,则您可能会在这里失去性能。测试和分析是唯一的方法。 - David Thornley
David Thornley:除非它是内联函数,否则它不会像本地变量获取那样快,因为编译器将不得不推送返回地址,跳转到代码的另一个部分(可能会出现缓存未命中等情况)。如果GetContext是内联getter或执行简单操作的宏,则只有这种情况下才能快速执行。 - Michael Kohne
@Michael Kohne:是的,但它可能是一个内联getter。许多getter在类定义中定义,这使它们默认为内联,并且编译器可以随意内联函数。 - David Thornley

8
如果逻辑上采用第二种方式是正确的,即多次调用GetContext()不会影响程序逻辑,那么即使你对其进行性能分析并证明两种方式没有差异,我也会采用第二种方法,这样下一个查看此代码的开发人员就不会再问同样的问题。

1
我同意这个观点。除了优化之外,对GetContext()的多次调用会迫使程序员询问GetContext()正在做什么,以至于需要被多次调用。 - Dan Breslau

2
显然,如果GetContext()具有副作用(I/O、更新全局变量等),那么建议的优化将产生不同的结果。
因此,除非编译器能够以某种方式检测到GetContext()是纯的,否则你应该自己进行优化。

1
如果你想知道编译器的作用,可以看汇编代码。

问题在于这显示了编译器现在正在做什么,而不是下一个版本将要做什么。你可以通过这种方式发现一些慢的地方(尽管汇编代码在性能上比以前更加不透明),但你无法找到一个构造是否在其他条件下是不安全的(比如下一个服务包)。 - David Thornley
嗯,是的。但如果他想知道编译器在做什么,汇编语言是一个不错的地方。 - Paul Nathan

0

这是一个非常简单的更改,我会做的。
修复它比辩论它更快。

但你真的有问题吗?
仅仅因为它经常被调用并不意味着它被调用得太频繁。
如果它看起来质量很差,尝试一下,看看它在花费时间方面的表现。
极有可能它不是你猜测的那样。


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