System.nanoTime()背后是什么?

在Java世界中,对System.nanoTime()的理解非常好。 总有一些人说它是快速,可靠的,并且在可能的情况下,应该使用它代替System.currentTimemillis()进行计时。 总的来说,他绝对是在撒谎,一点也不差,但是开发人员应该意识到一些缺点。 同样,尽管它们有很多共同点,但是这些缺点通常是特定于平台的。

视窗

使用QueryPerformanceCounter API实现功能,众所周知该API存在一些问题。 它可能会飞速发展 ,有人报告说在多处理器计算机上的速度可能非常慢 ,等等。我花了一些时间上网尝试查找QueryPerformanceCounter的工作原理和作用。 关于该主题尚无明确结论,但是有一些帖子可以简要介绍其工作原理。 我会说,最有用的,大概是人。 当然,只要稍作搜索,就能找到更多,但信息大致相同。

因此,如果可用,则实现似乎正在使用HPET 。 如果不是,则它将TSC与CPU之间的值进行某种形式的同步。 有趣的是, QueryPerformanceCounter承诺返回的值将以恒定的频率增加。 这意味着,在使用TSC和多个CPU的情况下,可能不仅会遇到一些困难,不仅因为CPU可能具有不同的TSC值,而且可能具有不同的频率。 请牢记所有注意事项Microsoft 建议使用SetThreadAffinityMask来阻塞将QueryPerformanceCounter调用到单个处理器的线程,这显然不会在JVM中发生。

LINUX

Linux与Windows非常相似,除了它更加透明(我设法下载了源代码:))。 该值从带有CLOCK_MONOTONIC标志的clock_gettime中读取(对于真正的男人,源可从Linux源的vclock_gettime.c中获得)。 使用TSCHPET 。 与Windows的唯一区别是Linux甚至不尝试同步从不同CPU读取的TSC值,而是按原样返回它。 这意味着该值可以在读取时依赖于CPU的依赖关系上来回跳。 另外,与Windows签约时,Linux不会保持更改频率恒定。 另一方面,它绝对应该提高性能。

索拉里斯

Solaris很简单。 我相信通过gethrtime可以实现与linux差不多相同的clock_gettime实现。 区别在于Solaris保证计数器不会跳回,这在Linux上是可能的,但是有可能返回相同的值。 从源代码可以看出,这种保证是使用CAS实现的,它需要与主存储器同步,并且在多处理器机器上可能相对昂贵。 与Linux相同,更改率可能有所不同。

结论

结论是多云之王。 开发人员必须意识到功能不是完美的,它可以向后或向前跳跃。 它可能不会单调变化,并且变化率会随CPU时钟速度的变化而变化。 而且,它并没有许多人想象的那么快。 在我的Windows 7计算机上进行单线程测试时,它仅比System.currentTimeMillis()快约10%,而在多线程测试中,线程数与CPU数相同,只是相同。 因此,总的来说,它所提供的只是分辨率的提高,这在某些情况下可能很重要。 最后要注意的是,即使CPU频率没有变化,也不要认为您可以将该值可靠地映射到系统时钟,请参见此处的详细信息。

附录

附录包含针对不同操作系统的功能实现。 源代码来自OpenJDK v.7。

的Solaris

// gethrtime can move backwards if read from one cpu and then a different cpu
// getTimeNanos is guaranteed to not move backward on Solaris
inline hrtime_t getTimeNanos() {
  if (VM_Version::supports_cx8()) {
    const hrtime_t now = gethrtime();
    // Use atomic long load since 32-bit x86 uses 2 registers to keep long.
    const hrtime_t prev = Atomic::load((volatile jlong*)&max_hrtime);
    if (now <= prev)  return prev;   // same or retrograde time;
    const hrtime_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&max_hrtime, prev);
    assert(obsv >= prev, "invariant");   // Monotonicity
    // If the CAS succeeded then we're done and return "now".
    // If the CAS failed and the observed value "obs" is >= now then
    // we should return "obs".  If the CAS failed and now > obs > prv then
    // some other thread raced this thread and installed a new value, in which case
    // we could either (a) retry the entire operation, (b) retry trying to install now
    // or (c) just return obs.  We use (c).   No loop is required although in some cases
    // we might discard a higher "now" value in deference to a slightly lower but freshly
    // installed obs value.   That's entirely benign -- it admits no new orderings compared
    // to (a) or (b) -- and greatly reduces coherence traffic.
    // We might also condition (c) on the magnitude of the delta between obs and now.
    // Avoiding excessive CAS operations to hot RW locations is critical.
    // See http://blogs.sun.com/dave/entry/cas_and_cache_trivia_invalidate
    return (prev == obsv) ? now : obsv ;
  } else {
    return oldgetTimeNanos();
  }
}

的Linux

jlong os::javaTimeNanos() {
  if (Linux::supports_monotonic_clock()) {
    struct timespec tp;
    int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp);
    assert(status == 0, "gettime error");
    jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec);
    return result;
  } else {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec);
    return 1000 * usecs;
  }
}

视窗

jlong os::javaTimeNanos() {
  if (!has_performance_count) {
    return javaTimeMillis() * NANOS_PER_MILLISEC; // the best we can do.
  } else {
    LARGE_INTEGER current_count;
    QueryPerformanceCounter(¤t_count);
    double current = as_long(current_count);
    double freq = performance_frequency;
    jlong time = (jlong)((current/freq) * NANOS_PER_SEC);
    return time;
  }
}

参考:


翻译自: https://www.javacodegeeks.com/2012/02/what-is-behind-systemnanotime.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值