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.

package main
import "time"
func main() {
    println("The current nanosecond is:", time.Now().Nanosecond())
}

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.

-Matt

Additionl Resources:
GCC
Go (and Google Go 6g Compiler)
gettimeofday()
C Standards Website

4 comments:

  1. 1<<31 - 1 > 1e9 you have arithmetic or typographic errors in your post

    ReplyDelete
    Replies
    1. Yes I did! Thank you very much. I missed a number in the middle, I must have copied the value you down incorrectly. Thank you again!

      Delete
  2. This was fixed the other day at tip: http://code.google.com/p/go/source/detail?r=42c8d3aadc40

    ReplyDelete
  3. Wow! Well, I guess I will edit the post now. Thanks.

    ReplyDelete