Time.now.usec in Windows
- Published March 3rd, 2008 in Tips & Tricks, Ruby
There are certain things that one programmer only thinks of when it comes across them. The one I’m about to tell is definitely the case.
I was deploying a custom made Ruby application on some Windows machines of a client. While testing the deployment, there were some weird errors that couldn’t be happening: duplicate keys while inserting a record on the database. The field causing the error was set as unique as it is a record id on a third party billing software. It was a timestamp including milliseconds.
Now, generating a timestamp up to the milliseconds level worked fine on my development machine (Mac OS X) and on my server machine (Linux/Ubuntu) as well, but it was failing miserably on Windows. Why’s that? Could it be that Windows doesn’t update its timer often enough? Gotcha!
For some unexplainable reason, Windows (Vista!) only updates its timer every 10ms, meaning that two consecutive requests for time will get the same timestamp. While on *nix machines you get time updates up to the nanosecond level, on Windows you have a long time before an update happens. And 10ms in the computer world is an eternity!
So here’s the quick fix for stopping this from happening. Just stick a Kernel.sleep(0.01) before the Time.now.usec call and you should be fine. It’s ugly, it makes you lose 10ms of your processing power but it’s a practical and fast fix.
P.S. - You can test this behavior by doing 10.times do puts Time.now.usec end on irb. Depending on your CPU, you might get up to 10 times the same time! On a low-end Dual Core HP laptop I got about 4 different times in 10 successive calls.




It is possible to tap into more powerful timers, just not from the Ruby VM:
http://technet.microsoft.com/en-us/sysinternals/bb897569.aspx
Instead of wasting CPU like that, consider some other solution…
And more here:
http://msdn2.microsoft.com/en-us/magazine/cc163996.aspx
Bruno,
Sure there are other ways. Here you go.
They just take way too much time for something that isn’t the programmers’ fault. Windows timer is simply poorly developed. It’s not a feature, it’s a bug.
How is it Windows’ fault if there’s a solution in C? Blame Ruby’s VM for not being able to tap into more powerful timers.
And for the record, the x86 architecture is not known for having good resolution timers - for historical reasons, they were never available. Linux does a very good job at it, but you cannot guarantee the same resolution across machines and so MS sticks to the common denominator, as big fat corps like to do. :D
Bruno,
Thing is, what’s the purpose of having multiple timers? Linux and most *nixes have one timer to rule them all and it seems more than enough, isn’t it?
You have to look beyond the “simple” when it comes to Windows. x86, as an architecture, is very botched and there is stuff in there that doesn’t make much sense but with a reason - for timers, it was IBM’s decision to cut 7.5US$ of the motherboard price in 1982. And MS got along with it for mostly backward compatibility reasons. The .NET runtime is also unable to reach high resolution timers - it’ simply not something appropriate for VM work…
Bruno,
It’s related to x86, but most *nix OSes have overcome that situation. Why wasn’t Microsoft able to do it too?
I don’t think the sleep solution will get you by in a multithreaded environment unless you are doing it inside a synchronized code section. Unless the sleep routine is just as whack and balances out the timer problem…
The Kernel.sleep was used within Rails, which is single-threaded.
mlopes,
Most *NIXes have overcome the situation, but with no guarantees - for example, I have a motherboard with high-res timers, but if I run the same code on another motherboard that does not possess them, it will fail miserably to run as it should. This is unacceptable for MS for support reasons, and so they stick to the common denominator.
Single-threaded? That’s a rather unconventional design decision. Now I really have to take a look on RoR.
dvnu,
Well, I think it’s not a feature but rather a defect. Anyway, that’s not the reason one should take a look at Rails.
About the Ruby interpreter, check out http://www.atdot.net/yarv/
Has cool slides in English - I hope this becomes the solution for Ruby v2.0, since it has already a lot of work in it.