【问题标题】:Performance overhead from System.getCurrentTimeMillis()System.getCurrentTimeMillis() 的性能开销
【发布时间】:2018-07-26 10:24:51
【问题描述】:

我正在制作一种概率模拟器,它将运行一定时间或一定数量的重复。我正在寻找优化它,它目前是多线程的,每个ProbabilityWorker 扩展Thread,主程序将自动分配n 线程,其中n 是但是许多线程可用(例如:on我的 Core i3-7100U,这是 4)。

我正在分析它的性能,我意识到我用来获取当前时间相对于结束时间的方法会导致大量开销。

对于可以“运行一定时间”的模式,我将new Date 对象作为循环条件的一部分,然后我将其更改为更快的System.currentTimeMillis() 以尝试节省时间,但我注意到即使这样也会产生开销。

我的run 函数如下所示:

public void run() {
    if (mode) {
        while (completed < repitions) {
            resultSet[randy.nextInt(o)]++;
            completed++;
        }
    } else {
        while (System.currentTimeMillis() < endTime) {
            resultSet[randy.nextInt(o)]++;
            completed++;
        }
    }
    done = true;
}

其中modetrue,如果运行一定数量的重复,randy 是随机数,o 是可能结果的数量,endTime 是以毫秒为单位的终点,系统时间 (可以修改,程序需要几秒钟,endTime是由当前时间加上secondsInput * 1000计算出来的。

另外,在同一个 Core i3-7100U 上,这些是我的性能统计:

DE-WEY-LAPTOP:/mnt/c/Users/danny/Documents/Programming/Data Structures/Probability$ java Main -n 10000000000

Running 10000000000 repitions of the probability simulator with 2 possible outcomes.
4 threads detected on system; doing 2500000000 repitions per thread.
Done. Gathering results from worker threads...
Done. Printing results...
Outcome 1: 4999997330 out of 10000000000 (49.9999733%)
Outcome 2: 5000002670 out of 10000000000 (50.0000267%)
Time taken: 43.443 seconds (2.301866813986143E8 ops/sec)

DE-WEY-LAPTOP:/mnt/c/Users/danny/Documents/Programming/Data Structures/Probability$ java Main -t 44

Running the probability simulator for 44 seconds using 4 threads.
Done. Gathering results from worker threads...
Done. Printing results...
Outcome 1: 141568074 out of 283130850 (50.000935609807264%)
Outcome 2: 141562776 out of 283130850 (49.999064390192736%)
Time taken: 44 seconds (6434792.045454546 ops/sec)

我的问题是,有没有办法优化System.currentTimeMillis() 调用以使其不存在或减少所需的时间?我可以使用另一个更快的呼叫吗?

【问题讨论】:

  • System.nanoTime() 更适合您的目的(确定某事需要多长时间)。另请参阅Time measuring overhead in Java
  • 您可以检查一秒钟内完成了多少次操作,并检查任何 N 次操作的时间。您可能会在这里或那里失去半秒,但它会有所改善;)例如,如果您的目标是数百万,您可以每 50k 次尝试进行一次计时检查。它将是:while(您的条件){ for 1:50000 { do that operations } }
  • 您在循环中调用System.currentTimeMillis() 283130850 次?我真的认为有更好的方法 - 实现 tick 并选择何时要检查给定时间小于当前时间。
  • “我用来获取当前时间相对于结束时间的开销很大” - 定义开销;这就是所谓的“忙循环”,即在分配的 CPU 时间允许的范围内调用多次的循环。您可能想要引入对 Thread.sleep() 的调用,以使该循环减少 CPU 占用 - 并减少同时完成的方法调用量。
  • 生成一个随机整数比 currentTimeMillis() 要昂贵得多。尝试分析您的代码。

标签: java performance optimization


【解决方案1】:

您真的应该研究System.nanoTime(并坚持下去)- 这是您在 JVM AFAIK 中可以获得的最佳结果。除了它在没有任何时钟时间概念的情况下测量经过的时间这一事实之外,它也是最快的 - 这就是JMH 使用它的原因(或我希望的任何其他理智的微基准)。

除了System.currentTimeMillis 返回ms 精度(并且有些事情比1ms 完成得更快)这一事实之外,两次调用此方法can return a negative value 之间的区别。

但有两件事要记住,首先是每次调用 System.nanoTime 都会对性能产生影响,平均而言(在我的 CPU 和 JVM-9 附近)25 ns 每称呼。

最后一点是System.nanoTime 具有纳秒精度,但没有纳秒精度。这意味着当您调用时:

long start = System.nanoTime();
long end = System.nanoTime();

两个结果都将返回一个具有纳秒精度的数字,即它们将有多个数字。

但是这个数字不可能很准确。嗯,根据你可能会问的准确。由于System.nanoTime 返回的值是任意的,因此没有可与之比较的值,除非对System.nanoTime 进行其他调用,因此:

long result = end - start;

是/可能不会是纳秒级的精确结果。其误差约为 1 微秒,在我的笔记本电脑上约为 0.2-0.5 微秒。


使用System.nanoTime,没有更快或更细粒度的东西。

【讨论】:

  • 好吧,除了我想让程序运行一定的时间,也就是“n”秒。
  • 此外,在这种情况下,实现System.nanoTime() 而不是System.getCurrentTimeMillis() 实际上要慢得多——我可以通过使用System.getCurrentTimeMillis() 获得每秒6,438,567 次操作,而System.nanoTime() 仅获得1,771 次操作/秒。
  • @DDPWNAGE 我只是想知道(此处插入讽刺的脸)如果那是您的代码在做一些可疑的事情,而不是nanoTimegetCurrentTimeMillis
  • 我自己也试过这个,nanoTime() 实际上比currentTimeMillis() 慢。原因解释here。在我的系统中,差异并不大。 currentTimeMillis() 可能快 3 倍。
  • 显然差异在很大程度上取决于操作系统;我正在使用currentTimeMillis()(显然)快的Windows。
猜你喜欢
  • 1970-01-01
  • 2016-12-19
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
  • 1970-01-01
  • 2011-03-16
  • 2013-02-24
相关资源
最近更新 更多