Go的time.Now().UnixNano()为什么返回一个较小的结果?

huangapple go评论143阅读模式
英文:

Why does Go‘s time.Now().UnixNano() return a smaller result?

问题

我使用time.Now().UnixNano()来计算RPC的时间,但我发现第二次调用time.Now().UnixNano()返回的时间戳比第一次调用的时间戳要小。

这段代码属于RPC客户端。我并发执行它数万次,有时会出现这种情况。

start := time.Now().UnixNano()
v := ck.Get(key)
end := time.Now().UnixNano()
if start > end {
   fmt.Printf("TimeReverseErr found: callTime %v > returnTime %v\n", start, end)
}

以下是输出结果:

... found: callTime 1667161706348148000 > returnTime 1667161706304057000
... found: callTime 1667161706365079000 > returnTime 1667161706319597000

操作系统:macOS Monterey 12.6
Go版本:go1.18 darwin/amd64

完整的代码如下:

// 在RPC客户端中
func Get(cfg *config, ck *Clerk, key string, log *OpLog, cli int) string {
   start := time.Now().UnixNano()
   v := ck.Get(key)
   end := time.Now().UnixNano()
   if start > end {
      fmt.Printf("TimeReverseErr found: callTime %v > returnTime %v", start, end)
   }
   cfg.op()
   if log != nil {
      log.Append(porcupine.Operation{
         Input:    models.KvInput{Op: 0, Key: key},
         Output:   models.KvOutput{Value: v},
         Call:     start,
         Return:   end,
         ClientId: cli,
      })
   }

   return v
}

我已经尝试了很多次,但问题仍然存在。

英文:

I use time.Now().UnixNano() to calculate the time of rpc, but I find that the second call to time.Now().UnixNano() returns a smaller timestamp than the first call.
This code belongs to the rpc client. I execute it concurrently tens of thousands of times and it sometimes comes up once.

start := time.Now().UnixNano()
v := ck.Get(key)
end := time.Now().UnixNano()
if start > end {
   fmt.Printf("TimeReverseErr found: callTime %v > returnTime %v\n", start, end)
}

Here are outputs:

... found: callTime 1667161706348148000 > returnTime 1667161706304057000
... found: callTime 1667161706365079000 > returnTime 1667161706319597000

OS: macOS Monterey 12.6<br>
Go: go1.18 darwin/amd64

The complete code:

// in RPC Client
func Get(cfg *config, ck *Clerk, key string, log *OpLog, cli int) string {
   start := time.Now().UnixNano()
   v := ck.Get(key)
   end := time.Now().UnixNano()
   if start &gt; end {
      fmt.Printf(&quot;TimeReverseErr found: callTime %v &gt; returnTime %v&quot;, start, end)
   }
   cfg.op()
   if log != nil {
      log.Append(porcupine.Operation{
         Input:    models.KvInput{Op: 0, Key: key},
         Output:   models.KvOutput{Value: v},
         Call:     start,
         Return:   end,
         ClientId: cli,
      })
   }

   return v
}

I have tried many times and this problem persists.

答案1

得分: 8

time.Now().UnixNano() 是一个墙钟读数,而不是单调钟读数。

> time: 单调时钟
>
> 操作系统提供了“墙钟”和“单调钟”两种时钟。墙钟受到时钟同步的影响,而单调钟则不受影响。一般规则是,墙钟用于显示时间,而单调钟用于测量时间。在这个包中,time.Now 返回的 Time 结构包含了墙钟读数和单调钟读数;后续的时间显示操作使用墙钟读数,而后续的时间测量操作,比如比较和减法,使用单调钟读数。
>
> 例如,下面的代码总是计算出大约 20 毫秒的正值时间间隔,即使在计时操作期间墙钟发生了变化:
>
> start := time.Now()
> ... 需要 20 毫秒的操作 ...
> t := time.Now()
> elapsed := t.Sub(start)


将你的代码改为使用 time.Now() 作为单调钟。例如:

package main

import (
	"fmt"
	"time"
)

func Get(key string) string { return "value" }

func main() {
	key := "key"

	start := time.Now()
	v := Get(key)
	end := time.Now()
	since := end.Sub(start)

	fmt.Println(start)
	fmt.Println(end)
	fmt.Println(since)
	if since < 0 {
		fmt.Println("start > end")
	}

	_ = v
}

m=+0.000018015 是一个单调钟值。

2022-10-31 00:38:30.415473095 -0400 EDT m=+0.000018015
2022-10-31 00:38:30.415473159 -0400 EDT m=+0.000018073
58ns
英文:

time.Now().UnixNano() is a wall clock reading, it is not a monotonic clock reading.

> time: Monotonic Clocks
>
> Operating systems provide both a “wall clock,” which is subject to changes for clock synchronization, and a “monotonic clock,” which is not. The general rule is that the wall clock is for telling time and the monotonic clock is for measuring time. Rather than split the API, in this package the Time returned by time.Now contains both a wall clock reading and a monotonic clock reading; later time-telling operations use the wall clock reading, but later time-measuring operations, specifically comparisons and subtractions, use the monotonic clock reading.
>
> For example, this code always computes a positive elapsed time of approximately 20 milliseconds, even if the wall clock is changed during the operation being timed:
>
> start := time.Now()
> ... operation that takes 20 milliseconds ...
> t := time.Now()
> elapsed := t.Sub(start)


Rephrase your code to use time.Now() as a monotonic clock. For example,

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func Get(key string) string { return &quot;value&quot; }

func main() {
	key := &quot;key&quot;

	start := time.Now()
	v := Get(key)
	end := time.Now()
	since := end.Sub(start)

	fmt.Println(start)
	fmt.Println(end)
	fmt.Println(since)
	if since &lt; 0 {
		fmt.Println(&quot;start &gt; end&quot;)
	}

	_ = v
}

m=+0.000018015 is a monotonic clock value.

2022-10-31 00:38:30.415473095 -0400 EDT m=+0.000018015
2022-10-31 00:38:30.415473159 -0400 EDT m=+0.000018073
58ns

huangapple
  • 本文由 发表于 2022年10月31日 11:35:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/74258492.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定