5

我最近开始学习 Go,我正在尝试实现一个可以由多个 groutines 并发使用的映射。我希望能够将我的实现与一个简单的sync.Mutex-protected 地图或类似的东西进行比较:https ://github.com/streamrail/concurrent-map/blob/master/concurrent_map.go

从使用 Google Caliper 开始,我假设一种天真的基准测试方法会允许许多不需要的优化来破坏实际结果。基准测试是否使用testing.B了一些技术来避免这种情况(毕竟 Go 和 Caliper 都是 Google 项目)?如果是,他们知道吗?如果不是,那么在 Go 中进行微基准测试的最佳方法是什么?

4

3 回答 3

4

将我的评论转换为答案。

为了完全准确,任何基准测试都应小心避免编译器优化消除被测函数并人为降低基准测试的运行时间。

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

我不是 100% 确定,但以下应该禁用优化?需要有更多经验的人来确认。

go test -gcflags=-N -bench=.
于 2016-05-02T05:27:46.857 回答
2

在 Java 中,由于 Hotspot 编译器的工作方式,微基准测试更难进行。如果您只是一遍又一遍地运行相同的代码,您通常会发现它变得更快,这会超出您的平均水平。作为补偿,Caliper 必须进行一些热身运行和其他技巧来尝试获得稳定的基准。

在 Go 中,事物是静态编译的。没有类似系统的运行时热点。它真的不需要做任何技巧来获得一个好的时机。

testing.B 功能应该不会影响代码的性能,因此您不必做任何特别的事情。

于 2016-05-01T14:47:24.523 回答
2

@David Budworth 提供了很多很好的信息,我同意 Go vs Java,但是在微基准测试中仍然需要考虑很多事情。他们中的大多数归结为“这与您的用例的匹配程度如何?” 例如,不同的并发模式在争用下的表现非常不同。您是否期望多个同时编写者很常见?一个作家,很多读者?读者多,写作少?单次访问?不同的生产者/消费者访问地图的不同部分?在您的基准测试中表现出色的方案对于其他用例可能是垃圾。

同样,您可能会发现您的方案是否非常依赖于参考位置。如果一遍又一遍地读取相同的值(因为它们保留在 CPU 上的缓存中),某些方法的性能会非常不同。这在微基准测试中很常见,但可能不能很好地说明您的预期用例。

这并不是说微基准没有用,只是说它们通常几乎没有用:D ……至少对于得出一般结论而言。如果您正在为特定项目构建它,只需确保您正在针对与您的用例匹配的真实数据和模式进行测试(理想情况下,只需将其变成您程序的真正基准,而不是“微基准”)数据结构)。如果您正在构建它以供一般使用,则需要确保您针对广泛的用例进行基准测试,然后才能得出太多关于它是否明显更好的结论。

如果它只是教育性的,那就太棒了。了解为什么特定方案在各种情况下效果更好或更差是很好的经验。只是不要把你的发现推到你的证据之外。

于 2016-05-01T15:19:10.013 回答