开始前,会这些就够了
- 知道开始值与结束值相减能得到差值
遇到不熟悉的词,先看这 2 个解释
- 时间戳
- 时间戳是用来标记某个时间点的数据。常见的一种做法是选定起点,再记录从起点算起的秒数或毫秒数;交换数据时,双方必须知道起点、单位和采用的时间规则。
- 单调时钟
- 单调时钟用于量出事情经过了多久,读数不会因为用户校正日期或系统调整日历时间而倒退。通常只比较同一时钟的两次读数,不把某次读数解释成具体年月日。
设想一段程序开始时记录电脑时间,结束后再减一次,日志却写着耗时负五百毫秒。程序没有穿越。一个可能的原因是运行期间系统时钟被向后校正了。这个假设案例能帮助我们区分两种名字都带时间、用途却不同的数值。
只改日历时钟,耗时会怎样?
任务实际经过多久和日历时钟被校正多少,是两个独立输入。这里全部使用假设数字。
| 时钟 | 开始 | 结束 | 相减 |
|---|---|---|---|
| 日历 | 10000 | 9500 | -500 |
| 单调 | 8000 | 8500 | 500 |
日历读数相减得到 -500 毫秒,同一单调时钟的读数相减得到 500 毫秒。负数来自时钟被向后校正,不是任务倒着运行。
按钮和滑块不读取或修改系统时间。不同机器、不同起点的单调读数不能直接相减;本模型也没有模拟休眠和计时分辨率。
先确定你要记录哪一种时间
墙上时间指能对应日期与钟点的时间,例如日志中的某天十点二十分。JavaScript 的 Date.now() 返回从约定起点到现在的毫秒数,依赖系统时钟。为了与真实日期保持一致,这个时钟可能被用户或校时服务调整,读数不保证始终向前。
单调时钟更像秒表,它的重点是同一时间基准下读数不倒退,而不是告诉你今天几号。浏览器的 performance.now() 返回相对自身时间起点的读数;Python 的 time.monotonic() 也用来比较经过的时长。两次相减有意义,单独一个读数未必能直接解释成日历日期。
用两组数字演一遍,不必真的改电脑时间
下面假设任务实际经过五百毫秒,但在这期间墙上时间被向后调了一千毫秒。日历读数净变化就是负五百,而秒表读数仍增加五百。所有数字都是为解释问题安排的模型输入,不是对你的电脑进行测量,也不会触碰系统设置。
如果只保留最终那个负数,就可能把错误归因于减法或业务代码。更有用的记录是同时保留用于展示的日期、用于计算耗时的差值,并注明单位和来源。这样查看日志的人才能知道,哪一个字段在回答发生时间,哪一个在回答持续多久。
const wallStart = 10000;
const elapsed = 500;
const clockAdjustment = -1000;
const wallEnd = wallStart + elapsed + clockAdjustment;
const monoStart = 8000;
const monoEnd = monoStart + elapsed;
console.log(wallEnd - wallStart);
console.log(monoEnd - monoStart);怎样运行:在浏览器控制台或 Node.js 中运行。它只计算预设数字,不读取或修改系统时间。
运行后对照结果
-500
500两个数能相减,前提是它们用同一把尺
一个网页打开后过了一秒,另一个网页可能已经打开一小时。若各自从不同起点计数,直接把它们的 performance.now() 相减,并不能得到某次网络请求的耗时。不同机器的单调读数也没有天然共享的零点,不能拿来替代跨机器的日期时间。
单位也要看清:浏览器这个接口用毫秒,Python 的 monotonic() 用秒,而带 _ns 的接口用纳秒表示。显示更多小数不保证测量更准确;浏览器还可能出于隐私和安全原因降低时间分辨率。很短操作连续测两次得到相同读数,并不表示它真的不耗时间。
秒表也有适用范围
测量时长之前,还要决定电脑休眠的那段时间算不算进去。不同 API、操作系统与浏览器对休眠期间计时的支持可能有差别,不能把“单调”直接理解为任何场景下都与现实经过的时间完全一致。较长的倒计时应把暂停和恢复的规则写清楚。
同样,预约回调在一秒后运行,不等于它恰好在第一千毫秒获得执行机会。其他任务会影响调度。若要比较两段程序快慢,应采用同一种时钟,重复观察并保持输入和环境尽量一致;一次小差值只能作为线索,不能替代可靠的性能结论。
记住这三点
- 记录日期与测量持续时间是两种需求,应选择对应的时钟。
- 相减前要确认读数的起点、单位和运行范围相同。
- 单调不等于无限精确,也不自动决定休眠和任务调度行为。
别把例子用过头
- 示例是时钟跳变的计算模型,没有宣称发生过一次真实故障。
- 不能把不同设备或不同起点的单调读数直接相减。
- 日期格式或时区的显示变化,与系统时钟实际调整不是同一件事。
自己试一小步
为一个本地任务设计日志,你会保存哪些时间字段?
- 分别列出用户需要看到的发生日期和你需要分析的耗时。
- 为每个字段注明来源、单位和可比较范围。
- 补充任务中途休眠、程序重启或日历时间被调整时的处理规则。
做到这些,就算完成
- 没有用一个字段同时冒充日历时间与可靠持续时长。
- 能解释哪些差值可以直接计算,哪些需要额外约定。
- 不需要真的修改系统时间来完成推演。
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- Date.now() (新标签页)↗
Date.now()的纪元与毫秒单位,以及浏览器可能降低精度。 - Performance: now() (新标签页)↗
单调时间与系统时钟调整的区别、
timeOrigin、休眠行为差异。 - time:时间的访问和转换 (新标签页)↗
单调时钟、未定义的参考点、秒与纳秒接口及 sleep 调度限制。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
MDN:量一段耗时,为什么要换一把钟 (新标签页)↗
先读与 Date.now 的比较,再读设备休眠限制;为短操作保存两次读数与差值。
查看学习准备与访问说明 →