Java 中时间测量开销

发布于 2024-11-01 01:09:58 字数 275 浏览 1 评论 0原文

当测量较低级别的经过时间时,我可以选择使用其中任何一种:

System.currentTimeMillis();
System.nanoTime();

这两种方法都是本机实现的。在深入研究任何 C 代码之前,有谁知道调用其中之一是否会产生大量开销?我的意思是,如果我真的不关心额外的精度,那么哪个精度会消耗更少的 CPU 时间?

注意:我使用的是标准 Java 1.6 JDK,但这个问题可能对任何 JRE 都有效......

When measuring elapsed time on a low level, I have the choice of using any of these:

System.currentTimeMillis();
System.nanoTime();

Both methods are implemented native. Before digging into any C code, does anyone know if there is any substantial overhead calling one or the other? I mean, if I don't really care about the extra precision, which one would be expected to be less CPU time consuming?

N.B: I'm using the standard Java 1.6 JDK, but the question may be valid for any JRE...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

聊慰 2024-11-08 01:09:59

本页上标记为正确的答案实际上不正确。由于 JVM 死代码消除 (DCE)、栈上替换 (OSR)、循环展开等原因,这不是编写基准测试的有效方法。只有像 Oracle 的 JMH 微基准测试框架这样的框架才能正确测量类似的东西。如果您对有效性有任何疑问,请阅读这篇文章此类微观基准。

() 的 JMH 基准测试:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
   @Benchmark
   public long currentTimeMillis() {
      return System.currentTimeMillis();
   }

   @Benchmark
   public long nanoTime() {
    return System.nanoTime();
   }
}

以下是结果(在 Intel Core i5 上):

Benchmark                            Mode  Samples      Mean   Mean err    Units
c.z.h.b.NanoBench.currentTimeMillis  avgt       16   122.976      1.748    ns/op
c.z.h.b.NanoBench.nanoTime           avgt       16   117.948      3.075    ns/op

以下是 System.currentTimeMillis()System.nanoTime >System.nanoTime() 速度稍快,每次调用约为 118 纳秒,而每次调用约为 123 纳秒。然而,同样清楚的是,一旦考虑到平均误差,两者之间的差异就很小了。结果也可能因操作系统而异。但总的结论应该是,它们在开销方面本质上是相当的。

2015/08/25 更新:虽然这个答案更接近正确的答案,但使用 JMH 进行测量,它仍然不正确。测量像 System.nanoTime() 这样的东西本身就是一种特殊的扭曲基准测试。答案和权威文章位于此处

The answer marked correct on this page is actually not correct. That is not a valid way to write a benchmark because of JVM dead code elimination (DCE), on-stack replacement (OSR), loop unrolling, etc. Only a framework like Oracle's JMH micro-benchmarking framework can measure something like that properly. Read this post if you have any doubts about the validity of such micro benchmarks.

Here is a JMH benchmark for System.currentTimeMillis() vs System.nanoTime():

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
   @Benchmark
   public long currentTimeMillis() {
      return System.currentTimeMillis();
   }

   @Benchmark
   public long nanoTime() {
    return System.nanoTime();
   }
}

And here are the results (on an Intel Core i5):

Benchmark                            Mode  Samples      Mean   Mean err    Units
c.z.h.b.NanoBench.currentTimeMillis  avgt       16   122.976      1.748    ns/op
c.z.h.b.NanoBench.nanoTime           avgt       16   117.948      3.075    ns/op

Which shows that System.nanoTime() is slightly faster at ~118ns per invocation compared to ~123ns. However, it is also clear that once the mean error is taken into account, there is very little difference between the two. The results are also likely to vary by operating system. But the general takeaway should be that they are essentially equivalent in terms of overhead.

UPDATE 2015/08/25: While this answer is closer to correct that most, using JMH to measure, it is still not correct. Measuring something like System.nanoTime() itself is a special kind of twisted benchmarking. The answer and definitive article is here.

用心笑 2024-11-08 01:09:59

我认为您不需要担心两者的开销。它是如此之小,以至于它本身几乎无法测量。下面是两者的快速微基准测试:

for (int j = 0; j < 5; j++) {
    long time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.currentTimeMillis();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.nanoTime();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    System.out.println();
}

最后的结果:

14297079ns per million
29206842ns per million

System.currentTimeMillis() 的速度确实是 System.nanoTime() 的两倍。然而,29 纳秒将比您测量的任何其他时间短得多。为了精度和准确性,我会选择 System.nanoTime(),因为它与时钟无关。

I don't believe you need to worry about the overhead of either. It's so minimal it's barely measurable itself. Here's a quick micro-benchmark of both:

for (int j = 0; j < 5; j++) {
    long time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.currentTimeMillis();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.nanoTime();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    System.out.println();
}

And the last result:

14297079ns per million
29206842ns per million

It does appear that System.currentTimeMillis() is twice as fast as System.nanoTime(). However 29ns is going to be much shorter than anything else you'd be measuring anyhow. I'd go for System.nanoTime() for precision and accuracy since it's not associated with clocks.

您应该只使用 System.nanoTime() 来测量运行某些内容所需的时间。这不仅仅是纳秒精度的问题,System.currentTimeMillis() 是“挂钟时间”,而 System.nanoTime() 用于计时而不是有与对方相同的“现实世界时间”怪癖。来自 System.nanoTime() 的 Javadoc:

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。

You should only ever use System.nanoTime() for measuring how long it takes something to run. It's not just a matter of the nanosecond precision, System.currentTimeMillis() is "wall clock time" while System.nanoTime() is intended for timing things and doesn't have the "real world time" quirks that the other does. From the Javadoc of System.nanoTime():

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.

源来凯始玺欢你 2024-11-08 01:09:59

System.currentTimeMillis() 通常非常快(据我所知,需要 5-6 个 cpu 周期,但我不知道我在哪里读过这篇文章),但它的分辨率在不同平台上有所不同。

因此,如果您需要高精度,请选择nanoTime(),如果您担心开销,请选择currentTimeMillis()

System.currentTimeMillis() is usually really fast (afaik 5-6 cpu cycles but i don't know where i have read this any more), but it's resolution varies on different plattforms.

So if you need high precision go for nanoTime(), if you are worried about overhead go for currentTimeMillis().

生寂 2024-11-08 01:09:59

如果您有时间,请观看 Cliff Click 的演讲,他谈到了 System.currentTimeMillis 的价格以及其他事情。

If you have time, watch this talk by Cliff Click, he speaks about price of System.currentTimeMillis as well as other things.

谁与争疯 2024-11-08 01:09:59

这个问题所接受的答案确实是不正确的。 @brettw 提供的替代答案很好,但仍然缺乏细节。

有关此主题的完整处理以及这些调用的实际成本,请参阅 https:// Shipilev.net/blog/2014/nanotrusting-nanotime/

回答所提出的问题:

有谁知道调用其中一个是否有大量开销?

  • 每次调用 System#nanoTime 的开销在 15 到 30 纳秒之间。
  • nanoTime 报告的值,即其分辨率,每 30 纳秒仅更改一次

这意味着,根据您是否尝试每秒执行数百万个请求,调用 nanoTime 意味着您'第二次调用 nanoTime 时,我们实际上丢失了很大一部分时间。对于此类用例,请考虑测量来自客户端的请求,从而确保您不会陷入协调遗漏,测量队列深度也是一个很好的指标。

如果您不想在一秒钟内塞满尽可能多的工作,那么 nanoTime 并不重要,但协调遗漏仍然是一个因素。

最后,为了完整性,无论其成本是多少,都不能使用currentTimeMillis。这是因为不能保证在两个调用之间继续进行。特别是在具有 NTP 的服务器上,currentTimeMillis 会不断移动。更不用说计算机测量的大多数东西都不需要一整毫秒。

The accepted answer to this question is indeed incorrect. The alternative answer provided by @brettw is good but nonetheless light on details.

For a full treatment of this subject and the real cost of these calls, please see https://shipilev.net/blog/2014/nanotrusting-nanotime/

To answer the asked question:

does anyone know if there is any substantial overhead calling one or the other?

  • The overhead of calling System#nanoTime is between 15 to 30 nanoseconds per call.
  • The value reported by nanoTime, its resolution, only changes once per 30 nanoseconds

This means depending if you're trying to do million of requests per seconds, calling nanoTime means you're effectively losing a huge chunk of the second calling nanoTime. For such use cases, consider either measuring requests from the client side, thus ensuring you don't fall into coordinated omission, measuring queue depth is also a good indicator.

If you're not trying to cram as much work as you can into a single second, than nanoTime won't really matter but coordinated omission is still a factor.

Finally, for completeness, currentTimeMillis cannot be used no matter what its cost is. This is because it's not guaranteed to move forward between two calls. Especially on a server with NTP, currentTimeMillis is constantly moving around. Not to mention that most things measured by a computer don't take a full millisecond.

短暂陪伴 2024-11-08 01:09:59

从理论上讲,对于使用本机线程并位于现代抢占式操作系统上的 VM,currentTimeMillis 可以实现为每个时间片仅读取一次。据推测,nanoTime 实现不会牺牲精度。

At a theoretical level, for a VM that uses native threads, and sits on a modern preemptive operating system, the currentTimeMillis can be implemented to be read only once per timeslice. Presumably, nanoTime implementations would not sacrifice the precision.

护你周全 2024-11-08 01:09:59

currentTimeMillis() 的唯一问题是,当您的虚拟机调整时间时,(这通常会自动发生)currentTimeMillis() 将随之调整,从而产生不准确的结果,特别是对于基准测试。

the only problem with currentTimeMillis() is that when your VM adjusts time, (this normally happens automatically) currentTimeMillis() will go with it, thus yielding unacurrate results especially for benchmarking.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文