website stat

Time.now.usec in Windows

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.


13 Responses to “Time.now.usec in Windows”

  1. Bruno
    Published at March 3rd, 2008 at 4:04 pm

    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…

  2. Bruno
    Published at March 3rd, 2008 at 4:07 pm

  3. mlopes
    Published at March 3rd, 2008 at 4:27 pm

    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.

  4. Bruno
    Published at March 3rd, 2008 at 5:03 pm

    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

  5. mlopes
    Published at March 3rd, 2008 at 5:14 pm

    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?

  6. Bruno
    Published at March 4th, 2008 at 12:10 am

    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…

  7. mlopes
    Published at March 4th, 2008 at 12:20 am

    Bruno,

    It’s related to x86, but most *nix OSes have overcome that situation. Why wasn’t Microsoft able to do it too?

  8. dvnu
    Published at March 4th, 2008 at 2:53 pm

    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…

  9. mlopes
    Published at March 4th, 2008 at 3:05 pm

    The Kernel.sleep was used within Rails, which is single-threaded.

  10. Bruno
    Published at March 5th, 2008 at 11:15 am

    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.

  11. dvnu
    Published at March 5th, 2008 at 11:22 am

    Single-threaded? That’s a rather unconventional design decision. Now I really have to take a look on RoR.

  12. mlopes
    Published at March 5th, 2008 at 12:26 pm

    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.

  13. Bruno
    Published at March 5th, 2008 at 2:32 pm

    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.