Go语言的时间精度到底有多高?

24

Go的time包声称提供纳秒级精度。 http://golang.org/src/pkg/time/time.go

我想知道它是如何实现的,我能否相信它。 我的疑虑来源于Python,它清楚地记录了时间的困难和限制:

来自http://docs.python.org/2/library/time.html

各种实时函数的精度可能低于其值或参数所表示的单位所建议的精度。例如,在大多数Unix系统上,时钟每秒只“打”50或100次。

另一方面,time()和sleep()的精度比它们的Unix等效函数更高:时间以浮点数表示,time()返回可用的最准确时间(使用Unix gettimeofday()(在可用时),而sleep()将接受具有非零小数部分的时间(在可用时使用Unix select()实现)。

由于操作系统给Python带来了如此多的困扰,Go如何实现纳秒级精度呢?


如果在2013年你问起Python是否真的使用了那些接口,那时它还停留在黑暗时代。Linux / glibc的clock_gettimenanosleep的手册页面都说它们自Linux 2.6或更早以来就存在,并且POSIX 2001甚至更早,比如POSIX 1993。(https://man7.org/linux/man-pages/man3/clock_gettime.3.html / https://man7.org/linux/man-pages/man2/nanosleep.2.html)。古老的C函数`time()`和`sleep()`并不适合进行比较。 - Peter Cordes
3个回答

40

关于实现方面,time.Now()会回退到在运行时中实现的函数。您可以查看C时间实现time·now的汇编实现(在这种情况下是linux amd64)。然后使用clock_gettime,它提供了纳秒分辨率。在Windows上,这是通过调用GetSystemTimeAsFileTime来实现的,它也生成纳秒(不如高分辨率但仍是纳秒级别)。

是的,分辨率取决于操作系统,您不能指望它在每个操作系统上都是准确的,但开发人员正在尽力使其尽可能好。例如,在go1.0.3中,time·now对于FreeBSD 使用 gettimeofday 而不是 clock_gettime,后者仅提供毫秒精度。您可以通过查看存储在AX中的值来查看此内容,因为它是系统调用id。如果您查看参考程序集,可以看到ms值乘以1000以获得纳秒值。然而,这是固定的现在

如果您想要确定,请检查运行时源代码中的相应实现,并查询您操作系统的手册。


1
此文档指出GetSystemTimeAsFileTime使用100纳秒间隔而非纳秒。 - George Polevoy
@GeorgePolevoy 当然,底层单位仍然是纳秒。时钟使用100纳秒的分辨率,因此它以100纳秒的步长递增,这使您可以计算纳秒,尽管不如单个纳秒精确。正如我在答案中所说的那样。 - nemo
被弃用的gettimeofday函数返回一个以微秒为单位的值。FreeBSD的gettimeofday函数真的只返回一个“粗略”的时间戳,只精确到毫秒而不是微秒吗? - Peter Cordes
你的代码链接 http://code.google.com/p/go/source/browse/src/pkg/runtime/sys_linux_amd64.s?name=go1.1.2#103 只是重定向到了 Github 存储库的主页:https://github.com/golang/go#103 。code.google.com 已关闭。我很好奇为什么他们会手动编写汇编包装器,但又不太好奇深入研究 Github。我猜如果他们仍然使用自己更简单但效率较低的调用约定,就无法像正常代码一样调用 libc 函数了吧? - Peter Cordes

18
Python的time.time函数存在一个问题,它返回一个float。浮点数是一种IEEE 754双精度数字,具有53位精度。
由于从1970-01-01(纪元)以来已经超过2 ** 30秒,因此需要61(31 + 30)位精度才能存储自1970-01-01以来准确到纳秒的时间。
不幸的是,这比python float可以存储的短7或8位,这意味着python浮点数始终比go时间不精确。
为了量化这一点,下面的演示表明,由于float类型的限制,python时间最多只能精确到100纳秒。
>>> t = time()
>>> t
1359587524.591781
>>> t == t + 1E-6
False
>>> t == t + 1E-7
True

所以,从一个int64开始,按nS计数没有这些限制,并且仅受底层操作系统的精度限制,如nemo所解释的那样。

0

如果您有兴趣查询操作系统以获取clock_gettime返回的值的精度,您可以使用适用于您的操作系统的系统调用包向clock_getres发起系统调用。例如,在Unix平台上,您可以执行以下操作:

package main

import (
    "fmt"
    "golang.org/x/sys/unix"
)

func main() {
    res := unix.Timespec{}
    unix.ClockGetres(unix.CLOCK_MONOTONIC, &res)
    fmt.Printf("Monotonic clock resolution is %d nanoseconds\n", res.Nsec)
}

单调时钟的值被时间包用于比较和涉及时间的操作;类似地,墙钟时间的精度可以通过将上面的unix.CLOCK_MONOTONIC更改为unix.CLOCK_REALTIME来获得。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接