前端Tips#4 - 用 process.hrtime 獲取納秒級的計時精度


本文同步自 JSCON簡時空 - 前端Tips 專欄#4,點擊閱讀

視頻講解

視頻地址

文字講解

如果去測試代碼運行的時長,你會選擇哪個時間函數? 一般第一時間想到的函數是 Date.nowDate.getTime

1、先講結論

在 Node.js 程序中,優先選 process.hrtime,其次選 performance.now,最后才會是 Date.now

之所以這么選,是基於 精度時鍾同步 兩方面考慮的。

2、知識講解

首先看一下 Date.now 的缺點

  1. 返回的時間精度為 毫秒(10^-3)級別,精度不夠;
  2. 受到系統時間影響,也有可能被其他軟件調整所影響

為了獲得更高精度、且和系統時間無關的時間,W3C 制定了 High Resolution Time Level 2 標准,其中的 6. Monotonic Clock 章節就規定了標准實現方需要提供 “單調遞增” 的全局系統時鍾:

單調遞增時鍾

在 Node.js 和 瀏覽器中都實現了該標准,具體的實現就是 performance 對象。我們可以通過 performance.now 獲取相對起點的時間戳,具備以下幾個特性:

  1. 和 JS 中其他可用的時間類函數(比如 Date.now )不同的是,performance.now() 返回的時間使用了一個浮點數來達到 微秒(10^-6) 級別的精確度
  2. 時間以一個 恆定的速率 慢慢 增加 的,它不會受到系統時間的影響(不會被其他軟件所調整)
  3. 從標准定義看,可以存在 clock drift (允許時鍾漂移)

時鍾漂移

這里大致說一下 clock drift 的概念,它是源於 時鍾同步 概念。時鍾同步(Clock synchronization)是計算機科學與工程學中的一個概念,旨在協調多個獨立的時鍾。現實中的多個時鍾,即使時間已調至一致,但在一段時間后依然會因為時鍾漂移(即clock drift)而顯示不同的時間,因為它們計時的速率會略有差異。

是否有更精細的時鍾存在呢?

有的,在 Node.js 環境中就提供了 process.hrtime 方法:

  1. 在 node v0.7.6 版本中新增,兼容性很好(畢竟現在都 v12 LTS 版本了)
  2. 精度高達 納秒(10^-9) 級別
  3. 不存在 時鍾漂移 (clock drift)

可以說 process.hrtime 方法是 專為測量時間間隔而打造 的。

注:瀏覽器環境沒有這個 hrtime 方法,因此瀏覽器環境所能達到的最高精度也就用 performance.now 的微秒級別(當然各個瀏覽器實現也是有差異)

只不過這個方法使用需要注意一下,首次調用返回的 time 需要作為后面調用的入參:

const NS_PER_SEC = 1e9;
const time = process.hrtime(); // 這里第一次調用,返回 time 變量
// [ 1800216, 25 ]

setTimeout(() => {
  const diff = process.hrtime(time); // 用第一次返回的 time 變量作為入參放在第二次調用中,從而獲取 diff 時間差值
  // [ 1, 552 ]

  console.log(`Benchmark took ${diff[0] * NS_PER_SEC + diff[1]} nanoseconds`);
  // Benchmark took 1000000552 nanoseconds
}, 1000);

到這里本節主要內容講完了,也就自然而然獲得本節剛開始的結論。

3、小知識

如果你使用 Node.js V10.7.0 以上的版本,還可以使用 hrtime.bigint 方法,它是 process.hrtimebigint 版本(bigint 類型從 v10.4 開始支持),返回當前的高精度實際時間。

這方法使用起來比 process.hrtime 更加方便,因為它不用額外的 time 入參,直接通過兩次調用結果相減就能獲得計算時間差:

const start = process.hrtime.bigint();
// 191051479007711n

setTimeout(() => {
  const end = process.hrtime.bigint();
  // 191052633396993n

  console.log(`基准測試耗時 ${end - start} 納秒`);
  // 基准測試耗時 1154389282 納秒
}, 1000);

4、參考文章


關於 “前端Tips專欄”

前端Tips”專欄,隸屬於 JSCON 專欄系列,設計初衷是快速獲取前端小技巧知識,取材廣泛,涵蓋前端編程諸多領域。有兩種方式獲取歷史 tips:

① 在公眾號內回“tips” + “年份” + “A(或者B)” 獲取半年度 tips。例如:回復 “tips2020A” 即可獲取 2020 年上半年 tips 列表

② 前往網站:https://boycgit.github.io/fe-program-tips ,里面提供了搜索功能

歡迎大家關注我的知識專欄,更多內容等你來挖掘

「可在微信內搜索 “JSCON簡時空”或 “iJSCON” 關注」


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM