Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my System.nanoTime() broken?

Myself and another developer on my time recently moved from a Core 2 Duo machine at work to a new Core 2 Quad 9505; both running Windows XP SP3 32-bit with JDK 1.6.0_18.

Upon doing so, a couple of our automated unit tests for some timing/statistics/metrics aggregation code promptly started failing, due to what appear to be ridiculous values coming back from System.nanoTime().

Test code that shows this behaviour, reliably, on my machine is:

import static org.junit.Assert.assertThat;

import org.hamcrest.Matchers;
import org.junit.Test;

public class NanoTest {

  @Test
  public void testNanoTime() throws InterruptedException {
    final long sleepMillis = 5000;

    long nanosBefore = System.nanoTime();
    long millisBefore = System.currentTimeMillis();

    Thread.sleep(sleepMillis);

    long nanosTaken = System.nanoTime() - nanosBefore;
    long millisTaken = System.currentTimeMillis() - millisBefore;

    System.out.println("nanosTaken="+nanosTaken);
    System.out.println("millisTaken="+millisTaken);

    // Check it slept within 10% of requested time
    assertThat((double)millisTaken, Matchers.closeTo(sleepMillis, sleepMillis * 0.1));
    assertThat((double)nanosTaken, Matchers.closeTo(sleepMillis * 1000000, sleepMillis * 1000000 * 0.1));
  }

}

Typical output:

millisTaken=5001
nanosTaken=2243785148

Running it 100x yields nano results between 33% and 60% of the actual sleep time; usually around 40% though.

I understand the weaknesses in accuracy of timers in Windows, and have read related threads like Is System.nanoTime() consistent across threads?, however my understanding is that System.nanoTime() is intended for exactly the purpose we're using it :- measuring elapsed time; more accurately than currentTimeMillis().

Does anyone know why it's returning such crazy results? Is this likely to be a hardware architecture problem (the only major thing that has changed is the CPU/Motherboard on this machine)? A problem with the Windows HAL with my current hardware? A JDK problem? Should I abandon nanoTime()? Should I log a bug somewhere, or any suggestions on how I could investigate further?

UPDATE 19/07 03:15 UTC: After trying finnw's test case below I did some more Googling, coming across entries such as bugid:6440250. It also reminded me of some other strange behaviour I noticed late Friday where pings were coming back negative. So I added /usepmtimer to my boot.ini and now all the tests behave as expected., and my pings are normal too.

I'm a bit confused about why this was still an issue though; from my reading I thought TSC vs PMT issues were largely resolved in Windows XP SP3. Could it be because my machine was originally SP2, and was patched to SP3 rather than installed originally as SP3? I now also wonder whether I should be installing patches like the one at MS KB896256. Maybe I should take this up with the corporate desktop build team?

like image 592
Chad Avatar asked Jul 18 '10 08:07

Chad


People also ask

What is System nanoTime ()?

nanoTime() method returns the current value of the most precise available system timer, in nanoseconds. The value returned represents nanoseconds since some fixed but arbitrary time (in the future, so values may be negative) and provides nanosecond precision, but not necessarily nanosecond accuracy.

How accurate is system nanoTime?

public static long nanoTime() // Returns the current value of the running JVM's high-resolution // time source, in nanoseconds. Pros: Highly precise. The time returned is around 1/1000000th of a second.

Is system nanoTime thread safe?

Summary: nanotime is slower(might take microseconds to execute), but more precise, but not threadsafe. currentTimeMillies is faster, perhaps less accurate but thread safe.

Is system nanoTime unique?

nanoTime() will return a unique value.


2 Answers

The problem was resolved (with some open suspicions about the suitability of nanoTime() on multi-core systems!) by adding /usepmtimer to the end of my C:\boot.ini string; forcing Windows to use the Power Management timer rather than the TSC. It's an open question as to why I needed to do this given I'm on XP SP3, as I understood that this was the default, however perhaps it was due to the manner in which my machine was patched to SP3.

like image 116
Chad Avatar answered Nov 10 '22 13:11

Chad


On my system (Windows 7 64-Bit, Core i7 980X):

nanosTaken=4999902563
millisTaken=5001

System.nanoTime() uses OS-specific calls, so I expect that you are seeing a bug in your Windows/processor combination.

like image 44
mikera Avatar answered Nov 10 '22 12:11

mikera