B3: Brain, Business & Body



Menu

Home
News
Code - Personal Mercurial Repo



gettimeofday(2) == bad; clock_gettime(2) == good.

Finally, a technical post I feel worthy of commenting on.

Performance of my application changes from time(3) to gettimeofday(2). Why?

Joe Open Source Developer

Doh! Really common question and should be an FAQ at this point.... or it is, but an under-published FAQ that only the performance wonks seem to know or run into as an issue. In short, gettimeofday(2) == EBAD && clock_gettime(2) == EGOOD. I think the best discussion on this is PHK's post titled Timers and timing, was: MySQL Performance 6.0rc1. Please read. Essentially, FreeBSD's gettimeofday(2) syscall is monotonic, highly accurate and precise, and Leenox's is not. Therefore, Leenox's gettimeofday(2) syscall is faster and less accurate than FreeBSD's. I happen to agree that gettimeofday(2) should be monotonic and I take issue with Leenox's gettimeofday(2) implementation. Monotonic time keeping requires CPU synchronization (which gettimeofday(2) does under FreeBSD and should be done under all operating systems) and will always be slow as a result. Most applications that do not require precise time synchronization across threads and CPUs should use clock_gettime(2) with a clock_id of CLOCK_REALTIME_FAST. Few applications actually need highly precise, highly accurate, monotonic time measurement and instead can skid by with highly precise, accurate, but non-monotomic time keeping. And now you know.

If you want a few additional links to look at the following posts.


Updated June 1, 2008 @20:45:00: I wrote a small time micro-benchmarking program (bench_time.c) to test out various time calls. When in doubt, test. Very interesting results.

% ./bench_time 9079882 | sort -rnk1
Timing micro-benchmark.  9079882 syscall iterations.
Avg. us/call    Elapsed     Name
9.322484    84.647053       gettimeofday(2)
8.955324    81.313291       time(3)
8.648315    78.525684       clock_gettime(2/CLOCK_REALTIME)
8.598495    78.073325       clock_gettime(2/CLOCK_MONOTONIC)
0.674194    6.121600        clock_gettime(2/CLOCK_PROF)
0.648083    5.884515        clock_gettime(2/CLOCK_VIRTUAL)
0.330556    3.001412        clock_gettime(2/CLOCK_REALTIME_FAST)
0.306514    2.783111        clock_gettime(2/CLOCK_SECOND)
0.262788    2.386085        clock_gettime(2/CLOCK_MONOTONIC_FAST)
Last value from gettimeofday(2): 1212380080.620649
Last value from time(3): 1212380161
Last value from clock_gettime(2/CLOCK_VIRTUAL): 2.296430000
Last value from clock_gettime(2/CLOCK_SECOND): 1212380338.000000000
Last value from clock_gettime(2/CLOCK_REALTIME_FAST): 1212380243.461081040
Last value from clock_gettime(2/CLOCK_REALTIME): 1212380240.459788612
Last value from clock_gettime(2/CLOCK_PROF): 185.560343000
Last value from clock_gettime(2/CLOCK_MONOTONIC_FAST): 5747219.271879584
Last value from clock_gettime(2/CLOCK_MONOTONIC): 5747216.886509281

Absolutely not what I was expecting, but very interesting none-the-less. clock_gettime(CLOCK_REALTIME_FAST, ...) is most certainly the way to go on FreeBSD. Hopefully this FAQ can be put to rest for a while.


Updated June 2, 2008 @10:45:00: I wrote a small variation to the above program that compares the values returned from the various time calls (bench_clock_realtime.c). On my dev system, CLOCK_REALTIME_FAST updates its values around 600 times a second, which is likely accurate enough for most applications, but may not be in all environments.

% ./bench_clock_realtime 9079882 | sort -rnk1
clock realtime micro-benchmark.  9079882 syscall iterations.
Avg. us/call	Elapsed		Name
9.317078	84.597968	gettimeofday(2)
8.960372	81.359120	time(3)
8.776467	79.689287	clock_gettime(2/CLOCK_REALTIME)
0.332357	3.017763	clock_gettime(2/CLOCK_REALTIME_FAST)
0.311705	2.830246	clock_gettime(2/CLOCK_SECOND)
Value from time(3): 1212427374
Last value from gettimeofday(2): 1212427293.590511	Equal: 0
Last value from clock_gettime(2/CLOCK_SECOND): 1212427460.000000000	Equal: 9079878
Last value from clock_gettime(2/CLOCK_REALTIME_FAST): 1212427457.656410126	Equal: 9078198
Last value from clock_gettime(2/CLOCK_REALTIME): 1212427454.639076390	Equal: 0
% irb
>> tot = 9079882
=> 9079882
>> eq = 9078198
=> 9078198
>> tot - eq
=> 1684
>> time = 3.017763
=> 3.017763
>> (tot - eq) / time
=> 558.029242190324