写点什么

如何正确地计算经过时间(elapsed time)

作者:redcoder54
  • 2023-09-22
    上海
  • 本文字数:1081 字

    阅读完需:约 4 分钟

假设我们要计算一段代码的执行时间,你会怎么做?在 Java 中,我们通常会这么做:


long t1 = System.currentTimeMillis();
// do someting
long t2 = System.currentTimeMillis();
// 执行时间long elaspedTime = t2 - t1;
复制代码


在绝大部分情况下,这样做是没有任何问题的,但如果我们遇到了时钟回拨,就可能出现elaspedTime为负数的情况,这是为什么?什么是始终回拨?


在解释时钟回拨前,我们先来了解下计算机中的时钟。每台计算机都有一个硬件设备(通常是石英晶体振荡器)用来计算时钟,但这些设备计算出来的时钟时间并不是准确的,就会导致有的机器时间快一点,有的机器时间慢一点,为了保证机器间的时间同步,每个机器都会通过 NTP(网络时间协议)与专用的时间服务器进行同步。


计算机中有两种时钟:日历时钟(time-of-day clock)和单调时钟(monotonic clock),二者都是用来衡量时间的,但却有着不同的目的。

日历时钟

日历时钟也叫挂钟时间(wall-clock time),是根据某个日历返回当前日期和时间,比如 Linux 中的 clock_gettime 和 Java 中的 System.currentTimeMillis() 返回自 epoch(UTC 时间 1970 年 1 月 1 日午夜))以来的毫秒数。


日历时钟通常需要和 NTP 时间服务器同步,来修正自己的时间。如果本地时间比服务器时间慢,就把时间往前调;如果本地时间快于服务器时间,就把时间往后调,这就是时钟回拨。不管是往前调还是往后调,都会导致时间跳跃。

单调时钟

单调时钟的时间是始终往前走的,不会往前或往后调,因此单调时钟适用于测量持续时间(经过时间),比如服务器响应时间、代码执行时间等。


Linux 上的 clock_gettime,和 Java 中的 System.nanoTime() 都是单调时钟。


现在我们回到开头的问题,如何计算一段代码的执行时间?正确的做法是用单调时钟,而不是日历时钟。以 Java 为例,我们应该这么做:


long t1 = System.nanoTime();
// do someting
long t2 = System.nanoTime();
// 执行时间long = t2 - t1;
复制代码

如何模拟时钟回拨

以 Java 语言为例,运行下面这段代码


long t1 = System.currentTimeMillis();
// 睡眠10秒钟,并10秒内修改本地电脑的时间,将时间设定在过去的某个时间点TimeUnit.SECONDS.sleep(10);
long t2 = System.currentTimeMillis();// 执行时间long elaspedTime = t2 - t1;System.out.println(elaspedTime);
复制代码


睡眠的目的是让我们有足够的时间去修改本地电脑的时间,模拟时钟回拨,如果在 t1 后将系统时间修改到过去某个时间,程序运行结束后,你会看到 elaspedTime 是一个小于 0 的值。


如果你把 System.currentTimeMillis() 换成 System.nanoTime(),不管你怎么调整时间,elaspedTime 永远是一个略大于 10s 的数值。


发布于: 刚刚阅读数: 7
用户头像

redcoder54

关注

还未添加个人签名 2018-07-19 加入

还未添加个人简介

评论

发布
暂无评论
如何正确地计算经过时间(elapsed time)_Java_redcoder54_InfoQ写作社区