Golang 基准测试 b.StopTimer() 挂起——是我吗?

标签 go

我在对我的 Go 程序进行基准测试时遇到了问题,在我看来它像是一个 Go 错误。请帮助我理解为什么不是。

简而言之:当我调用 b.StopTimer() 时基准测试挂起,即使我随后调用了 b.StartTimer()。 AFAICT 我正在按预期使用这些功能。

这是在 Mac OSX 10.11.5“El Capitan”上,go version go1.6.1 darwin/amd64;其他一切似乎都很好。我将在今天晚些时候尝试在 Linux 上检查并更新问题。

这是代码。像往常一样使用 go test -bench=. 运行。

package bmtimer

import (
    "testing"
    "time"
)

// These appear to not matter; they can be very short.
var COSTLY_PREP_DELAY = time.Nanosecond
var RESET_STATE_DELAY = time.Nanosecond

// This however -- the measured function delay -- must not be too short.
// The shorter it is, the more iterations will be timed.  If it's infinitely
// short then you will hang -- or perhaps *appear* to hang?
var FUNCTION_OF_INTEREST_DELAY = time.Microsecond

func CostlyPrep()         { time.Sleep(COSTLY_PREP_DELAY) }
func ResetState()         { time.Sleep(RESET_STATE_DELAY) }
func FunctionOfInterest() { time.Sleep(FUNCTION_OF_INTEREST_DELAY) }

func TestPlaceHolder(t *testing.T) {}

func BenchmarkSomething(b *testing.B) {

    CostlyPrep()
    b.ResetTimer()

    for n := 0; n < b.N; n++ {

        FunctionOfInterest()
        b.StopTimer()
        ResetState()
        b.StartTimer()
    }
}

提前感谢任何提示!我在 Google 和此处都表现不佳。

编辑: 看来这只发生在 FunctionOfInterest() 非常快;是的,我特别想在基准测试循环中执行此操作。我更新了示例代码以使其更加清晰。我认为我现在想通了。

最佳答案

TL;DR:这不是错误,只是有点棘手。

我很确定我知道发生了什么。在基准测试循环中使用 StopTimerStartTimer 不是问题;问题是我想测试的功能太快了。或者无论如何都是为了我好。

通过调整该函数使用的休眠时间,我们可以很容易地观察到基准测试差异。至少对于这么简单的事情,measured 代码返回的速度越快,Go 运行的迭代越多。这是已知行为,我早该想到。一个人发现这样的输出:

PASS
BenchmarkSomething-4     1000000          2079 ns/op
ok      bmtimer 36.587s

FunctionOfInterest 返回得非常快——或者当然是“无限”快,如在 func tooFast() {} 中——然后 Go 会继续运行更多迭代它,试图获得一个可靠的基准。你可能会失去耐心。或者,您的计算机可能:

*** Test killed with quit: ran too long (10m0s).
FAIL    bmtimer 600.037s

(这是在一个内核上 100% CPU 的情况下十分钟。)

当然,好消息是这不是错误;坏消息是,它不只是停留在例如一百万次迭代并告诉您它的最佳猜测,这仍然很烦人。

不过话又说回来,另一个好消息是遇到这个问题表明你的函数足够快,可能不需要基准测试。

剩下的谜团是:为什么只有当我们停止和启动计时器时才会发生这种情况?

无需摆弄计时器,我可以调用我的 noop 函数,它可以很好地对其进行基准测试,仅需 20 亿次迭代:

BenchmarkSomething-4    2000000000           0.33 ns/op
ok      bmtimer 0.706s

我最好的猜测是基于@JimB 的评论——停止世界至少需要一点时间,而当您运行 20 亿次迭代时,很容易就会超过 10 分钟。就像那个人说的:这里有 10 亿,那里有 10 亿,很快你就会谈论真正的钱。

(无论如何这是我的猜测。比我聪明的人可能会纠正我。)

因此我们故事的寓意是:一定要利用b.StopTimer()b.StartTimer() 以便在您没有其他方法处理状态时处理状态。但请记住,对于非常快的函数,对于基准测试程序来说可能太多了。

我真希望我知道一种更好的方法来隔离快速函数的特定函数基准。我在尝试 StopTimer 之前使用的最初 hack 是有另一个基准函数,它只对额外的代码进行基准测试,所以你会得到两个数字:ns/op 和 ns/op 没有感兴趣的功能。这行得通,而且可能比停止计时器更准确,但它确实需要提前了解如何解释基准测试输出。

关于Golang 基准测试 b.StopTimer() 挂起——是我吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37620251/

相关文章:

go - 扁平化主要依赖的供应传递依赖

regex - 如何使用 bson 在 GoLang 中为正则表达式编写 mongodb 查询?

go - 重置结构的某些属性的惯用方法是什么?

英雄联盟。庆典 : apiworker: command not found

go - 如何将图片转换为image.RGBA?

nginx - Gorilla WebSocket 一分钟后断开连接

api - API调用不以JSON响应,而是以一些废话数据响应

go - filepath.Abs​​() 在结果中不提供子目录

go - 比较等于真,但是当我把它当作条件时,为什么它没有被评估为真?

json - 如何制作包含 [] 数组的 [] 数组和映射