正确测量Go中的持续时间

maj*_*aja 23 time go

在Go中精确测量持续时间的正确方法是什么?大多数应用程序只使用标准时间包和以下方法:

var startTime = time.Now()
doSomeHardWork()
var duration = time.Since(startTime) // or: time.Now() - startTime
Run Code Online (Sandbox Code Playgroud)

但是,time.Now()返回当前系统时间,这会导致两个缺陷:

  1. 如果在测量期间更改了系统时间(例如由于时区更改(DST)或闰秒),则生成的持续时间也是错误的.
  2. 系统时间可以比实时更快或更慢地打勾.当操作系统将内部时钟与NTP时间服务器同步时(这可能每小时发生几次!),这种情况总会发生.

    来自MSDN:

    [时间服务]调整本地时钟速率,使其收敛到正确的时间.如果本地时钟和[准确时间样本]之间的时间差太大而无法通过调整本地时钟速率来校正,则时间服务将本地时钟设置为正确的时间.

如果系统时间更改(手动或由于DST),则可能会检测到无效持续时间并将其丢弃.但是,如果系统时钟速度比世界时间快10%,则几乎不可能检测到.这是预期的行为以及系统时钟的设计方式.

因此,大多数其他语言都提供了用于测量持续时间的专用API:

在Go中精确测量执行时间的正确方法是什么?

pet*_*rSO 27

包装时间

单调时钟

操作系统既提供"挂钟",也可以改变时钟同步,而"单调时钟"则不然.一般规则是挂钟用于告知时间,单调时钟用于测量时间.而不是拆分API,在这个包中Time by Time.Now包含挂钟读数和单调时钟读数; 后来的时间操作使用挂钟读数,但后来的时间测量操作,特别是比较和减法,使用单调时钟读数.

例如,即使在定时操作期间更改挂钟,此代码也始终计算大约20毫秒的正经过时间:

start := time.Now()
... operation that takes 20 milliseconds ...
t := time.Now()
elapsed := t.Sub(start)
Run Code Online (Sandbox Code Playgroud)

其他成语,例如time.Since(start),time.Until(截止日期)和time.Now().Before(截止日期),对于挂钟重置同样强大.

从Go 1.9开始(2017年8月24日发布),Go使用单调时钟作为持续时间.

请参阅提案:Go中的单调经过时间测量.

  • 这不是意味着如果`a` 和`b` 是时间,`b - a + a != b` 是可能的吗? (4认同)
  • 这是一个非常有趣的API设计.比提供单独的功能并依赖程序员选择正确的功能(许多人没有)要好得多. (2认同)
  • 您可以使用 `elapsed := time.Since(start)` 来代替 `elapsed := t.Sub(start)` 并跳过对 `Now()` 的第二次调用 (2认同)

Ken*_*ant 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)
Run Code Online (Sandbox Code Playgroud)

其他成语,例如time.Since(start),time.Until(截止日期)和time.Now().Before(截止日期),对于挂钟重置同样强大.

问题触发了pkg 时间的这一变化,这促使此提议从Russ Cox进行更改:

比较和减去时间观察到的时间.如果在两个观察值之间重置系统挂钟,则现在可能返回不正确的结果.我们建议延长time.Time表示以保持额外的单调时钟读数以用于那些计算.除了其他好处之外,这应该使得基本的经过时间测量不可能使用time.Now和time.Since报告负面持续时间或其他不基于现实的结果.


maj*_*aja 5

对于Go 1.8及之前,正确的计时功能不在时间包内,而是在运行时包中:

func nanotime() int64
Run Code Online (Sandbox Code Playgroud)

为了正确测量执行时间,应使用以下过程:

var startTime = runtime.nanotime()
doSomeHardWork()
var duration = runtime.nanotime() - startTime
Run Code Online (Sandbox Code Playgroud)

不幸的是,该方法本身没有很好地记录.经过长时间的讨论后,它出现在这个问题上,如果它真的是必要的话.对于Go 1.9及更新版本,请参阅Kenny Grant的回答.