我最近开始学习Go,我试图实现一个可以由多个涂料同时使用的地图。我希望能够将我的实现与简单的sync.Mutex
- 受保护的地图进行比较,或者类似于此:https://github.com/streamrail/concurrent-map/blob/master/concurrent_map.go
从使用Google Caliper,我认为一种简单的基准测试方法会允许许多不需要的优化来废弃实际结果。是否使用testing.B
采用某些技术来避免这种情况的基准(毕竟Go和Caliper都是谷歌项目)?如果是的话,他们知道吗?如果没有,那么Go中微基准测试的最佳方法是什么?
答案 0 :(得分: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
}
以下页面也很有用。
Compiler And Runtime Optimizations
另一个有趣的读物是
另一个有趣的标志是-N,它将禁用优化 传入编译器。
我不是100%肯定,但以下应禁用优化?有经验的人需要确认一下。
go test -gcflags=-N -bench=.
答案 1 :(得分:2)
在Java中,由于Hotspot编译器的工作原理,微基准测试更难实现。如果你只是一遍又一遍地运行相同的代码,你会发现它变得更快,从而抛出你的平均值。为了弥补这一点,Caliper必须进行一些预热运行和其他技巧以试图获得稳定的基准。
在Go中,事物是静态编译的。没有运行时Hotspot之类的系统。它并不需要做任何技巧来获得良好的时机。
testing.B功能应该对您的代码性能没有影响,因此您不必做任何特别的事情。
答案 2 :(得分:2)
同样,您可能会发现您的方案是否非常依赖于参考的位置。如果反复读取相同的值,则某些方法的执行方式会有很大差异(因为它们保留在CPU上的高速缓存中)。这在微基准测试中非常常见,但可能并不能很好地说明您的预期用例。
这并不是说微基准测试是无用的,只是它们经常几乎无用:D ......至少是为了得出一般结论。如果您正在为特定项目构建此项,请确保您正在测试与您的用例匹配的实际数据和模式(理想情况下,只需将其转换为程序的真实基准,而不是“微基准”数据结构)。如果你正在构建这个用于一般用途,你需要确保你对各种用例进行基准测试,然后才能得出太多关于它是否更好的结论。
如果它只是教育性的,那真棒。了解为什么特定方案在各种情况下更好或更差,这是很棒的经验。只是不要将你的发现推过证据。