Pages

Monday, November 12, 2012

Go Go Gadget Now().Nanosecond()

Before I even get started, the "issue" I discuss below was resolved just three days ago. Of course, there will still be some time before this makes it to the next release of Go and also when GCC updates their libraries. Thanks Brad for pointing this out! I had no idea. Well, I don't want to delete this post... so for historical reasons it follows:

Time. It is such an unusual thing. I suppose it is "unusal" as it being
such an abstract quantity. I cannot just reach out into the air and pull down
some time. Yet, we humans try to quantize the abstract. After all, humans like
to understand how things work. And we like to measure the things that do work,
or measure things to figure out why they work the way they do. In other
words: we quantize time, break it up into distinct bits and pieces, so that
we can measure and understand it.

The more we chop-up time, the more accurate we can define something in all four
dimensions (three dimensions of space, and one of time). For instance, I can tell a
person to meet me at a particular location on the earth, which has 3 dimensions
(e.g. meet me on floor 4 of the building at corner of Foo and Bar streets). But
when should my acquaintance meet me? Well, I tell them a time. Possibly a
really really precise time, at 7PM and 4 seconds and 3001 nanoseconds. I'm a
robot. Anyways, I am digressing from the purpose of this post.

It is not uncommon for programming languages to provide a way (library) to
access time. This time comes from the system that is executing the program.
For instance, "gettimeofday()" and "clock_gettime()" are just two such functions
that ask a POSIX (e.g. Linux, BSD...) system for what the time is. These two
functions return a value that represents time. But how precise the result is, defines how fine you can
measure an event. I want to look at the time routine provided by the Go programming language.

The following is my analysis of the Go 1 "time" package's "Now()" function, as provided by gcc 4.7.1 and 4.7.2 for a 64bit x86 architecture. We investigate the possible lack of precision from the resulting Nanosecond member. This
is not an analysis of how the Google 6g compiler and provided Go 1 "time"
package implements things. I did peer through the Go 1 "time" package provided
by the 6g Google Go compiler version 1.0.3. It seems that their routine works
similarly, so for all intensive purposes, we can assume what I write below for
gcc is similar to the 6g compiler.

Firstly, I notice that the Go API provides a function
for getting the nanoseconds from the current time of day. That's nifty. The
following code should accomplish this.

The above function obtains the current time, and then returns which nanosecond of the
current second the time is. See, time, in this case the second, can be divided into
smaller increments. The API says that the nanosecond returned is the
nanosecond of the current second of time. Now, recall that a nanosecond
represents just 1^-9 (0.000000001) of a second. That's a pretty darn small
increment of time. This means that the "time" package (library) of
Go should use a data structure large enough to contain 1^9 or
1000000000 values.

Well, a look at the Go runtime library (gcc and 6g)
uses a 32-bit signed integer (int) to represent the current
nanosecond of the current second. As any good data-structure-athiest-skeptic
needs to ask is: "Are 32-bits large enough to hold a value of 1000000000?"
Actually, is the "int" data type wide enough to contain the maximum value of a
nanosecond? Remember that an "int" is signed, so one bit does not represent a time,
rather that single bit is dedicated to representing a positive/negative
(signedness) state of the value. I can't imagine a negative time, and
zombie-Einstein might puke if you were to venture into the past.

The time library for Go provided by gcc uses the "gettimeofday()" system call, or virtual dynamic shared object vDSO, function provided by the operating system. A POSIX compliant operating system should provide the latter routine which obtains the time from the system. The Go "time" library calls this routine to obtain time. Go will then (convert) the returned value into a Go structure of time. I did
not see in the Google 6g version of the library where they make an explicit call
to "gettimeofday()." However, I did see where
the library accesses members of the result, which are similar as to what "gettimeofday()"
returns.

An integer can hold a range of values and has to be large enough to at least contain the values of such a range. 31-bits of
value and one to represent signedness. Go implements "int" as being 32-bits, is is the data structure used for storing nanoseconds. So 2^31 - 1 or a maximum value of 2147483647. Which... YES... is large enough to contain enough nanoseconds for a second. (I had a typo in my original post and I thank the anonymous poster who cleared that up for me).

The POSIX standard defines that the "gettimeofday()" function returns
seconds and MICROSECONDS... I'll say that again, microseconds, since the epoch of
bell-bottoms and afro-sheen (January 1, 1970). In fact, the POSIX standard is a
bit loose, and says it should return at least seconds and microseconds (leaving
the doors open for other values). But, a browse of the code in gcc and 6g shows that they take a microsecond value and treat that as a base for nanoseconds.

So, what then is the Go "time" library doing? Well, when we call "Now()" we get a "Time" object. This object is initialized by an architecture specific routine which queries the system (using a system call) and sets-up the "Time" object, including the nanosecond member, from the result of that system call. What the x86 64-bit Linux implementations seems to be doing, for the "Now()" routine is effectively querying "gettimeofday()" and then taking the microsecond value and multiplying it by 1000 to represent nanoseconds. So, for those using the nanosecond value returned by a "Time" object created by "Now()", the last three digits will be zero. This "Time" object can be further manipulated (e.g. using "time.Add()", "time.Date(), or "time.Unix()") which might modify the nanosecond value, presenting a more precise representation of nanoseconds. This means that the initial return of time from "Now()" will have a nanosecond value that is always missing the lower three digits (and bits) of the value. Well, it turns out that 999999000, the maximum value of nanoseconds Go can return by the "Time" object created by "Now()" is still .99 of a second, but it's just not the high precision that might be desired. See, the lower three bits are missing... that latter value
converted to binary: 0b111011100110101100011000011000

For most people this is not a big deal. But being aware is always a good thing. However, if you are using "Now()" for nanoseconds, you might in fact be looking for a high precision version of time.

TLDR; The Go runtime "time" package returns a scaled microsecond value to
represent nanoseconds and not the actual nanoseconds to a high granularity, when time is created using the "Now()" library function. The
lower three digits (1000 values) are always zero.