我和我的另一位开发人员最近从工作中的 Core 2 Duo 机器转移到了新的 Core 2 Quad 9505;两者都运行带有 JDK 1.6.0_18 的 Windows XP SP3 32 位。
这样做后,我们对一些计时/统计/指标聚合代码的一些自动化单元测试立即开始失败,因为从 System.nanoTime() 返回的值似乎很荒谬。
在我的机器上可靠地显示这种行为的测试代码是:
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));
}
}
典型输出:
millisTaken=5001
nanosTaken=2243785148
运行 100 倍会产生实际睡眠时间的 33% 到 60% 之间的纳米结果;不过通常在 40% 左右。
我了解 Windows 中计时器准确性的弱点,并已阅读相关线程,例如System.nanoTime() 跨线程是否一致?,但是我的理解是 System.nanoTime() 正是为了我们使用它的目的而设计的:-测量经过的时间;比 currentTimeMillis() 更准确。
有谁知道为什么它会返回如此疯狂的结果?这可能是硬件架构问题(唯一改变的主要是这台机器上的 CPU/主板)?我当前的硬件存在 Windows HAL 问题?JDK问题?我应该放弃 nanoTime() 吗?我应该在某处记录错误,还是对如何进一步调查提出任何建议?
更新 19/07 03:15 UTC:在尝试了下面 finnw 的测试用例后,我做了一些谷歌搜索,遇到了诸如bugid:6440250 之类的条目。这也让我想起了周五晚些时候我注意到的其他一些奇怪的行为,其中 ping 恢复为负数。所以我将/usepmtimer添加到我的 boot.ini 中,现在所有测试都按预期运行。我的 ping 也正常。
不过,我对为什么这仍然是一个问题感到有些困惑;根据我的阅读,我认为 TSC 与 PMT 的问题在 Windows XP SP3 中得到了很大的解决。可能是因为我的机器原本是 SP2 的,并且被修补到 SP3 而不是最初安装为 SP3 吗?我现在也想知道我是否应该安装像MS KB896256这样的补丁。也许我应该与公司桌面构建团队一起讨论这个问题?