< backgound>
我正处在一个真正需要优化C++代码的阶段。我正在为分子模拟编写一个库,我需要添加一个新的特性。过去,我已经尝试过添加这个特性,但是后来我使用了嵌套循环中调用的虚拟函数。我对此有不好的感觉,第一个实现证明了这是个坏主意。然而,这是可以测试的概念。
< /background>
现在我需要这个功能尽可能快(好吧,没有汇编代码或GPU计算,这仍然必须是C++和更多可读性而不是更少)。现在,我对模板和类策略有了更多的了解(来自Alexandrescu的优秀著作),我认为编译时代码生成可能是解决方案。
然而,在完成将其实现到库中的巨大工作之前,我需要对设计进行测试。问题在于测试这一新特性的效率的最佳方法。
显然,我需要打开优化,因为如果没有这个g++ (可能还有其他编译器),目标代码中就会保留一些不必要的操作。我还需要大量使用基准测试中的新特性,因为1e-3秒的增量可以区分好的设计和糟糕的设计(这个特性在实际程序中将被称为百万次)。
问题是,g++有时在优化时“太聪明”,如果它认为计算结果从未被使用,它可以删除整个循环。在查看输出程序集代码时,我已经看到过一次。
如果我将一些打印添加到stdout中,那么编译器将被迫在循环中执行计算,但我可能主要是对iostream实现进行基准测试。
那么,如何对从库中提取的一个小特性执行正确的基准测试呢?相关问题:在一个小单元上进行这种体外测试是一种正确的方法,还是我需要完整的上下文?
谢谢你的建议!
似乎有几种策略,从允许微调的编译器特定选项到更通用的解决方案,这些解决方案应该适用于每个编译器,比如volatile
或extern
。
我想我会尝试所有这些。非常感谢你所有的答案!
发布于 2009-01-13 07:09:17
如果要强制任何编译器不丢弃结果,请将结果写入易失性对象。根据定义,无法优化该操作。
template<typename T> void sink(T const& t) {
volatile T sinkhole = t;
}
没有iostream开销,只需在生成的代码中保留一个副本。现在,如果您正在收集许多操作的结果,最好不要一个一个地丢弃它们。这些副本仍然可以增加一些开销。相反,以某种方式收集单个非易失性对象中的所有结果(因此需要所有单独的结果),然后将结果对象分配给易失性对象。例如,如果您的单个操作都生成字符串,则可以通过将所有char值一起添加到模块化1<<32来强制求值。这几乎不会增加任何开销;字符串可能在缓存中。加法的结果随后会被分配给易失性,所以每个刺中的每一个字符实际上都必须被计算,不允许捷径。
发布于 2009-01-12 06:51:35
除非您有一个真正积极的编译器(可能发生),否则我建议计算一个校验和(简单地将所有结果加在一起)并输出校验和。
除此之外,您可能需要在运行任何基准测试之前查看生成的程序集代码,以便直观地验证是否正在实际运行任何循环。
发布于 2009-01-12 07:10:03
编译器只允许消除不能发生的代码分支.只要不排除要执行一个分支,它就不会消灭它。只要在某个地方存在某些数据依赖项,代码就会在那里运行。编译器在估计程序的哪些方面将不会运行时并不太聪明,也不尝试运行,因为这是一个NP问题,很难计算。他们有一些简单的检查,比如if (0)
,但仅此而已。
我的基本观点是,您可能前面遇到了其他问题,例如C/C++计算布尔表达式的方式。
但是无论如何,因为这是关于速度的测试,所以您可以自己检查是否调用了它--运行它一次,然后用返回值进行另一次测试。或者一个静态变量被递增。在测试结束时,打印出生成的数字。结果是相等的。
来回答你关于试管测试的问题:是的,去做吧。如果你的应用程序时间如此紧迫,那就去做吧。另一方面,您的描述暗示了一个不同的问题:如果您的三角洲处于1e-3秒的时间范围内,那么这听起来就像是一个计算复杂性问题,因为所讨论的方法必须经常被调用(对于很少的运行,1e-3秒是可以忽略的)。
您正在建模的问题域听起来非常复杂,而且数据集可能很大。这样的事情总是很有趣的。不过,首先要确保你有正确的数据结构和算法,然后再对你想要的一切进行微观优化。因此,我要说的是,首先看一下整个上下文。 ;-)
出于好奇,你在计算什么问题?
https://stackoverflow.com/questions/435627
复制