在Go中精确测量持续时间的正确方法是什么? 大多数应用程序只使用标准时间包和以下方法:
var startTime = time.Now()
doSomeHardWork()
var duration = time.Since(startTime) // or: time.Now() - startTime
但是,time.Now()
会返回当前系统时间,这会导致两个缺陷:
系统时间可以比实时更快或更慢地打勾。这种情况总是发生在操作系统将内部时钟与NTP时间服务器同步时(可能每小时发生几次!)
来自MSDN:
[时间服务]调整本地时钟速率以允许它 收敛到正确的时间。如果之间的时差 本地时钟和[准确时间样本]太大而无法纠正 调整本地时钟速率,时间服务设置本地时钟 到了正确的时间。
如果系统时间更改(手动或由于DST),则可能会检测到无效持续时间并将其丢弃。但是,如果系统时钟滴答,例如与世界时间同步快10%,实际上无法检测到。这是预期的行为以及系统时钟的设计方式。
出于这个原因,大多数其他语言提供了用于测量持续时间的专用API:
System.nanoTime()
,System.currentTimeMillis()
相当于time.Now()
而且错了System.Diagnostics.Stopwatch
QueryPerformanceCounter
和QueryPerformanceFrequency
is_steady
成员常量为true
std::chrono::steady_clock
或std::chrono::high_resolution_clock
performance.now()
,而使用new Date()
错误在Go中精确测量执行时间的正确方法是什么?
答案 0 :(得分:27)
单调时钟
操作系统提供“挂钟”,这是受制于的 时钟同步的变化,以及“单调时钟” 不。一般规则是挂钟用于告诉时间和 单调时钟用于测量时间。而不是拆分API, 在这个包中Time by time.Now包含一个墙 时钟读数和单调时钟读数;后来说实话 操作使用挂钟读数,但稍后进行时间测量 操作,特别是比较和减法,使用 单调时钟阅读。
例如,此代码始终计算正的经过时间 大约20毫秒,即使挂钟改变了 在定时操作期间:
start := time.Now() ... operation that takes 20 milliseconds ... t := time.Now() elapsed := t.Sub(start)
其他成语,例如time.Since(start),time.Until(截止日期),以及 time.Now()。之前(截止日期),对壁钟同样强大 复位。
从Go 1.9开始(2017年8月24日发布),Go使用单调时钟作为持续时间。
答案 1 :(得分:6)
这可以在Go 1.9(2017年8月)中使用单调时钟,您无需做任何特别的事情就可以从中受益:
https://tip.golang.org/pkg/time/#hdr-Monotonic_Clocks
操作系统提供“挂钟”,这是受制于的 时钟同步的变化,以及“单调时钟” 不。一般规则是挂钟用于告诉时间和 单调时钟用于测量时间。而不是拆分API, 在这个包中Time by time.Now包含一个墙 时钟读数和单调时钟读数;后来说实话 操作使用挂钟读数,但稍后进行时间测量 操作,特别是比较和减法,使用 单调时钟阅读。
例如,此代码始终计算正的经过时间 大约20毫秒,即使挂钟改变了 在定时操作期间:
start := time.Now()
... operation that takes 20 milliseconds ...
t := time.Now()
elapsed := t.Sub(start)
其他成语,例如time.Since(start),time.Until(截止日期),以及 time.Now()。之前(截止日期),对壁钟同样强大 复位。
此time pkg的更改由此issue触发,这促使此proposal更改为来自Russ Cox:
按时间观察的时间的比较和减法。现在可以返回 如果在两者之间重置系统挂钟,则结果不正确 观察结果。我们建议将time.Time表示扩展到 保持额外的单调时钟读数用于那些 计算。除了其他好处之外,这应该是不可能的 使用time.Now和time.Since的基本经过时间测量 报告负面持续时间或其他不以现实为基础的结果。
答案 2 :(得分:5)
对于Go 1.8及之前,正确的计时功能不在时间包内,而是在runtime package中:
func nanotime() int64
为了正确测量执行时间,应使用以下程序:
var startTime = runtime.nanotime()
doSomeHardWork()
var duration = runtime.nanotime() - startTime
不幸的是,该方法本身没有很好地记录。它在this issue之后出现在long discussion if it was really neccessary。 对于Go 1.9及更新版本,请参阅Kenny Grant的答案。