Go测试中的B基准是否能防止不必要的优化?

7

我最近开始学习Go语言,正在尝试实现一个可以被多个groutine并发使用的映射。我想将我的实现与简单的sync.Mutex保护的映射或类似https://github.com/streamrail/concurrent-map/blob/master/concurrent_map.go的东西进行比较。

通过使用Google Caliper,我发现一个天真的基准测试方法会允许许多不必要的优化来破坏实际结果。使用testing.B的基准测试是否采用了一些技术来避免这种情况(毕竟Go和Caliper都是Google项目)?如果是,它们是已知的吗?如果不是,那么在Go中进行微基准测试的最佳方式是什么?


2
我不确定这是否是您要寻找的内容。请阅读此链接中的“有关编译器优化的说明”部分:http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go - John S Perayil
@JohnSPerayil,这正是我正在寻找的!我只是想知道它是否详尽(我认为不是),以及它是否仍然有效(我不确定)。 - Elsinor
这可能是有效的,但不是详尽无遗的。 - John S Perayil
3个回答

5

将我的评论转换为回答。

为了完全准确,任何基准测试都应该注意避免编译器优化排除测试函数,从而人为地降低基准测试的运行时间。

var result int

func BenchmarkFibComplete(b *testing.B) {
        var r int
        for n := 0; n < b.N; n++ {
                // always record the result of Fib to prevent
                // the compiler eliminating the function call.
                r = Fib(10)
        }
        // always store the result to a package level variable
        // so the compiler cannot eliminate the Benchmark itself.
        result = r
}

来源

以下页面也可能有用。

编译器和运行时优化

另一个有趣的阅读材料是:

另一个有趣的标志是 -N,它将禁用编译器中的优化 传递。

来源1 来源2

我不确定,但以下内容应该禁用优化?需要更多经验的人确认。

go test -gcflags=-N -bench=.

虽然我无法确认所有情况(或找到发布说明来解释),但上述示例不再按指定方式执行。没有副作用的版本没有经过优化,两个版本返回相同的值。 - Corey Scott

2
在Java中,由于Hotspot编译器的工作方式,微基准测试更加困难。如果您只是简单地反复运行相同的代码,往往会发现它变得更快,从而影响您的平均值。为了补偿这一点,Caliper必须进行一些预热运行和其他技巧,以尝试获得稳定的基准测试。
在Go中,事物是静态编译的。没有像Hotspot系统那样的运行时。它不需要执行任何技巧来获取良好的计时。
testing.B功能不应对您的代码性能产生影响,因此您不需要做任何特殊处理。

谢谢,听起来很鼓舞人心,尽管John S Perayil在他对我的回答的评论中提供的链接显示,至少需要手动防御一些优化才能进行testing.B基准测试。不过,该链接已经超过2年了,所以现在可能已经发生了变化。 - Elsinor

2
@David Budworth提供了很多有用的信息,我同意他对Go与Java的看法,但在微基准测试中,你仍然需要考虑许多因素。其中大部分都归结为“这与你的使用情况有多接近?”例如,不同的并发模式在争用下表现非常不同。您是否预计多个同时写入者很常见?单个写入者,多个读取者?许多读取者,很少写入?单一访问?不同的生产者/消费者访问地图的不同部分?在您的基准测试中表现出色的方案可能对其他用例来说是垃圾。
同样,您可能会发现您的方案对引用局部性的依赖程度很高或很低。如果重复读取相同的值(因为它们保留在CPU缓存中),某些方法的执行效果会非常不同。这在微基准测试中非常普遍,但可能不太适合您的预期用例。
这并不是说微基准测试是无用的,只是它们往往几乎是无用的:D...至少不能得出一般性结论。如果您正在为特定项目构建此内容,请确保针对与您的用例匹配的实际数据和模式进行测试(最好将其转换为程序的真实基准测试,而不是数据结构的“微基准测试”)。如果您正在为一般用途构建此内容,则需要确保针对各种用例进行基准测试,然后才能得出是否显着更好的结论。
如果只是教育性质的,那太棒了。了解为什么特定方案在各种情况下表现更好或更差是很好的经验。只是不要把你的发现推过头。

谢谢,这是一篇非常好的文章,我几乎同意其中的所有内容,但我不认为它回答了我的问题 :) - Elsinor
将如何在Go中进行最佳微基准测试的根本问题概括为“在与您的用例匹配的测试中使用真实数据”,这就是答案。如果唯一的问题是“如何避免类似Java热点优化扭曲”,请参见David的答案(完全正确)。在Go中没有这样的问题,因此没有问题。John的链接(以及从那里链接的链接)也非常出色,可能正是您问题的关键。 - Rob Napier

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