23

Go's time package claims giving a nanosecond precision. http://golang.org/src/pkg/time/time.go

I'm wondering how it is implemented and if I can trust it. My doubts come from Python, which clearly documents its difficulties and limitations with time:

From http://docs.python.org/2/library/time.html

The precision of the various real-time functions may be less than suggested by the units in which their value or argument is expressed. E.g. on most Unix systems, the clock “ticks” only 50 or 100 times a second.

On the other hand, the precision of time() and sleep() is better than their Unix equivalents: times are expressed as floating point numbers, time() returns the most accurate time available (using Unix gettimeofday() where available), and sleep() will accept a time with a nonzero fraction (Unix select() is used to implement this, where available).

Since the operating systems are giving such a hard time to python, how does Go achieve its nanosecond precision?

Niriel
  • 2,605
  • 3
  • 29
  • 36
  • If Python in 2013 when you asked this was really using those interfaces, it was stuck in the dark ages. The Linux / glibc man pages for `clock_gettime` and `nanosleep` both say they've been in since Linux 2.6 or earlier and POSIX 2001 if not earlier like POSIX 1993. (https://man7.org/linux/man-pages/man3/clock_gettime.3.html / https://man7.org/linux/man-pages/man2/nanosleep.2.html). The ancient C functions called `time()` and `sleep()` aren't an appropriate comparison. – Peter Cordes Jun 17 '23 at 08:45

3 Answers3

38

Well as for the implementation, time.Now() falls back to a function implemented in the runtime. You can review the C time implementation and the implementation for time·now in assembly (linux amd64 in this case). This then uses clock_gettime, which provides nano seconds resolution. On windows, this is realized by calling GetSystemTimeAsFileTime, which too generates nanoseconds (not as high res but nanoseconds).

So yes, the resolution depends on the operating system and you can't expect it to be accurate on every OS but the developers are trying to make it as good as it can be. For example, in go1.0.3, time·now for FreeBSD used gettimeofday instead of clock_gettime, which only offers millisecond precision. You can see this by looking at the value stored in AX, as it is the syscall id. If you take a look at the referenced assembly, you can see that the ms value is mulitplied by 1000 to get the nanoseconds. However, this is fixed now.

If you want to be sure, check the corresponding implementations in the runtime source code and ask the manuals of your operating system.

Community
  • 1
  • 1
nemo
  • 55,207
  • 13
  • 135
  • 135
  • 1
    https://support.microsoft.com/en-us/help/188768/info-working-with-the-filetime-structure this doc indicates the GetSystemTimeAsFileTime works with 100 nanosecond intervals, not nanoseconds – George Polevoy Sep 04 '17 at 19:04
  • @GeorgePolevoy Of course the underlying unit is still nanoseconds. The clock uses a resolution of 100 ns, therefore it increments in 100ns steps which enables you to count in nanoseconds, although not as precise as a single nanosecond. Just as I said in the answer. – nemo Sep 04 '17 at 19:23
  • The deprecated `gettimeofday` function returns a value in microseconds. Does FreeBSD's `gettimeofday` really only return a "coarse" timestamp that's only precise to the millisecond, not microsecond? – Peter Cordes Jun 17 '23 at 08:49
  • Your code links like http://code.google.com/p/go/source/browse/src/pkg/runtime/sys_linux_amd64.s?name=go1.1.2#103 just redirects to the main page of the github repo: https://github.com/golang/go#103 . code.google.com is shut down. I was curious why they'd hand-write asm wrappers, but not curious enough to dig in github. I guess if they still use their own simpler but less-efficient calling convention, they couldn't call libc functions like normal code could? – Peter Cordes Jun 17 '23 at 08:54
18

One of the problems with Python's time.time function is that it returns a float. A float is an IEEE 754 double-precision number which has 53 bits of precision.

Since it is now more than 2**30 seconds since 1970-01-01 (the epoch) you need 61 (31 + 30) bits of precision to store time accurate to the nanosecond since 1970-01-01.

Unfortunately that is 7 or 8 bits short of what you can store in a python float, meaning that python floats will always be less precise than go time.

To quantify that the demonstration below shows that python time is at most accurate to 100nS just due to the limitations of the float type.

>>> t = time()
>>> t
1359587524.591781
>>> t == t + 1E-6
False
>>> t == t + 1E-7
True

So go, starting with an int64 and counting in nS doesn't have these limitations and is limited to the precision of the underlying OS as explained very well by nemo.

Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
0

If you are interested in querying the operating system to get the precision of the values returned by clock_gettime, you can make a syscall to clock_getres using the syscall package appropriate to your operating system. For example, on Unix platforms you can do:

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)
}

Values from the monotonic clock are used by the time package for comparisons and operations involving times; the precision of the wall-clock time is similarly obtained by changing unix.CLOCK_MONOTONIC in the above example to unix.CLOCK_REALTIME.