如何在Node.js中获取微秒时间?

125

如何在Node.js中获取最精确的时间戳?

附言:我的Node.js版本是0.8.X,node-microtime扩展对我来说不起作用(安装时崩溃)。

15个回答

203
在Node.js中,“高分辨率时间”可以通过process.hrtime获得。它返回一个数组,第一个元素是以秒为单位的时间,第二个元素是剩余的纳秒数。
要获取当前时间的微秒数,请执行以下操作:
var hrTime = process.hrtime()
console.log(hrTime[0] * 1000000 + hrTime[1] / 1000)

(感谢 itaifrenkel 指出上述转换中的错误。)

在现代浏览器中,可以使用微秒精度的时间戳 performance.now。有关说明,请参见https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

我在 Node.js 中基于 process.hrtime 实现了此函数,相对而言其难度较大,特别是如果您想在程序中计算两个时间点之间的时间差。请参阅http://npmjs.org/package/performance-now。按照规范,此函数以毫秒为单位报告时间,但它是带有亚毫秒精度的浮点数。

在版本 2.0 中,此模块报告的毫秒数是相对于节点进程启动时的时间(Date.now() - (process.uptime() * 1000))。如果您想要类似于 Date.now() 的时间戳,则需要将其加入结果中。另请注意,您不应重新计算 Date.now() - (process.uptime() * 1000)。对于精确测量,Date.nowprocess.uptime 都不太可靠。

要获取当前时间的微秒数,可以使用类似以下的代码。

var loadTimeInMS = Date.now()
var performanceNow = require("performance-now")
console.log((loadTimeInMS + performanceNow()) * 1000)

另请参阅:JavaScript是否提供高分辨率计时器?


37
值得注意的是,要进行基准测试,您可以将先前调用 process.hrtime() 的结果传递给它以获取时间差。例如: var startTime = process.hrtime(),然后执行操作后,使用 var diff = process.hrtime(startTime) 来获取时间差。 - Justin Warkentin
5
在将秒转换为微秒时,请检查时间单位。 console.log(hrTime [0] * 1000000 + hrTime [1] / 1000) - itaifrenkel
感谢您指出了这个错误,itaifrenkel!我已经更正了转换代码。 - Myrne Stol
2
require("performance.now") 应该改为 require("performance-now"),对吗? - UpTheCreek
8
process.hrtime()不返回当前时间。 - user2609094
1
Node还在perf_hooks模块中内置了performance API的实现。 - janispritzkau

135

node v10及以上版本:您应该使用process.hrtime.bigint(),它会产生一个单一的BigInt数字而不是数组。 process.hrtime()已被标记为“legacy”。

低于node v10的版本:如vaughan所述,在Node.js中可用process.hrtime() - 其分辨率为纳秒,因此其更高。此函数返回一个包含当前实时高分辨率值的数组[seconds, nanoseconds],但请注意,它未绑定到任何特定的时钟,这意味着两个连续值之间的差异告诉您经过了多长时间,但单个值对您没有任何有意义的信息。

其他JS环境new Date().getTime()?这将为您提供毫秒级的时间戳。 更新:


1
@Esailija 任何小于毫秒的时间都变得极不可靠。 - TheZ
你的意思是什么?比时间戳更精确?(以毫秒为单位) - Dominic Barnes
5
我必须同意。这个答案是完全错误的。请参考 @Meryn Stol 的回答获取正确的答案。 - Justin Warkentin
21
process.hrtime() 这些时间是相对于过去某个任意时间的,与当天时间无关,因此不受时钟漂移的影响。主要用于测量间隔之间的性能。因此它不会提供当前时间。 - Alex
17
所有排名靠前的答案都是错误的。 process.hrtime() 不会返回当前时间。 - user2609094
显示剩余2条评论

24
now('milli'); //  120335360.999686
now('micro') ; // 120335360966.583
now('nano') ; //  120335360904333

已知 now 是:

const now = (unit) => {
  
  const hrTime = process.hrtime();
  
  switch (unit) {
    
    case 'milli':
      return hrTime[0] * 1000 + hrTime[1] / 1000000;
      
    case 'micro':
      return hrTime[0] * 1000000 + hrTime[1] / 1000;
      
    case 'nano':
    default:
      return hrTime[0] * 1000000000 + hrTime[1];
  }
  
};

27
process.hrtime() 不返回当前时间。 - user2609094
你用的是哪个操作系统? - Abdennour TOUMI
警告: "micro" 和 "nano" 看起来都将溢出数字。为了使它们正常工作,您可能需要先将它们转换为 BigInt。 - DoomGoober
@user2609094 是正确的。从文档中可得:"这些时间是相对于过去某个任意时间的,主要用途是测量间隔之间的性能"(即_不是_当前时间)。此外也可查看这个答案 - Alex

20
自 Node.js 10.7.0 起,支持BigInt数据类型(另请参见blog post announcement)。对于这些支持的Node.js版本,process.hrtime([time])方法现在被视为“遗留”,已被process.hrtime.bigint()方法替换。

bigint 版本的 process.hrtime() 方法返回当前高分辨率实时时间的 bigint

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

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

  console.log(`Benchmark took ${end - start} nanoseconds`);
  // Benchmark took 1154389282 nanoseconds
}, 1000);

简述:

  • Node.js 10.7.0+ - 使用process.hrtime.bigint()
  • 其他情况 - 使用process.hrtime()

我发现 process.hrtime.bigint() 没有返回正确的时间,看起来不是 Unix 时间戳,而且年份是 2043,例如 2325734153489354。 - Matan Tubul
直接从文档(https://nodejs.org/api/process.html#processhrtimetime)中引用:“这些时间是相对于过去的任意时间而言的,并且与当天的时间无关,因此不受时钟漂移的影响。主要用途是在间隔之间测量性能。” - d4nyll

11
你还可以使用适用于NodeJS和浏览器的性能API:
var start = performance.timing ?
    performance.timing.navigationStart :
    performance.timeOrigin;

var time = (performance.now() + start) * 1000;

性能API将值存储为浮点数,小数部分是微秒。
编辑:使用现代JavaScript,您可以使用以下代码:
const start = performance?.timing ?? performance.timeOrigin;
const time = (performance.now() + start) * 1000;

这是Node@^8.9的唯一正确答案。 - Jason

10

OP 表示他们尝试使用这个软件包,但无法安装。 - Cameron Tacklind
@CameronTacklind 他们确实这样做了 - 他们称之为“扩展”,这让我错过了这个。由于很多人通过这个问题来阅读本文 - 即如何在节点上执行微秒 - 回答仍然有用。 - mikemaccana

3

Node.js nanotimer

我在process.hrtime函数调用的基础上,为node.js编写了一个包装库/对象。它具有有用的功能,例如对同步和异步任务进行计时,可以指定以秒、毫秒、微秒甚至纳秒为单位,并遵循内置javascript计时器的语法,使其更加熟悉。

计时器对象也是离散的,因此您可以拥有任意数量的计时器对象,每个计时器都有自己运行的setTimeoutsetInterval进程。

它被称为nanotimer。快来看看吧!


1

为了比 Date.now() 更精确地工作,但使用浮点精度的毫秒:

function getTimeMSFloat() {
    var hrtime = process.hrtime();
    return ( hrtime[0] * 1000000 + hrtime[1] / 1000 ) / 1000;
}

3
process.hrtime() 不会返回当前时间。 - Marc J. Schmidt
你是对的。从文档中可以看到:“这些时间是相对于过去的任意时间而言的,并且与当天的时间无关,因此不会受到时钟漂移的影响。主要用于测量时间间隔之间的性能。” https://nodejs.org/api/process.html#process_process_hrtime_time - Ross

1

现在唯一的方法是调用第三方库。

  1. 使用编写在C语言上的带有编译函数的库(语言不重要,重要的是操作系统的系统调用)。可以自己编写,也可以使用例如https://www.npmjs.com/package/microtime(源代码https://github.com/wadey/node-microtime/blob/master/src/microtime.cc)。
  2. 使用date +%s%N生成一个进程,在Linux上可直接使用。可以通过使用require('child_process').exec实现。由于此解决方案的性能,我无法确定时间的准确性。

注意process.hrtime与当前时间无关,

这些时间是相对于过去某个任意时间的,与当天时间无关。 [https://www.geeksforgeeks.org/node-js-process-hrtime-method/]


Linux 上的子进程听起来不错。 - pirs

1

编辑:

使用 jcubic 或 Alexandr 在下面的答案: https://dev59.com/12gt5IYBdhLWcg3w5xg_#69870775 https://dev59.com/12gt5IYBdhLWcg3w5xg_#72741299


我对这个解决方案并不感到自豪,但你可以通过以下方式获得微秒或纳秒时间戳
const microsecond = () => Number(Date.now() + String(process.hrtime()[1]).slice(3,6))
const nanosecond = () => Number(Date.now() + String(process.hrtime()[1]).slice(3))

// usage
microsecond() // return 1586878008997591
nanosecond()  // return 1586878009000645600

// Benchmark with 100 000 iterations
// Date.now: 7.758ms
// microsecond: 33.382ms
// nanosecond: 31.252ms

请注意:

  • 此解决方案仅适用于node.js,
  • 它比Date.now()慢约3到10倍,
  • 奇怪的是,它似乎非常准确,hrTime似乎完全遵循js时间戳的刻度。
  • 您可以将Date.now()替换为Number(new Date())以获取毫秒级时间戳。

编辑:

这里提供了一个解决方案,以获得带有逗号的微秒,但是,数字版本将由javascript本地进行四舍五入。因此,如果您想要每次使用相同的格式,则应使用其字符串版本。

const microsecondWithCommaString = () => (Date.now() + '.' + String(process.hrtime()[1]).slice(3,7))
const microsecondWithComma = () => Number(Date.now() + '.' + String(process.hrtime()[1]).slice(3,7))

microsecondWithCommaString() // return "1586883629984.8997"
microsecondWithComma() // return 1586883629985.966

这样是行不通的。想象一下,hrtime当前为999.999微秒。你调用Date.now(),得到1234567890。现在你调用hrtime,但它已经翻转到0了,所以现在你得到的时间不是接近于1234567891.001的时间,而是1234567890.001。你已经倒退了一毫秒的时间。单独来看,你可能不会在意,但如果你试图计时一个需要100微秒的任务,你就会在偶尔出现-900微秒,这不仅会严重破坏你的统计数据,你的代码也可能无法正确处理负时间。 - Jason
@Jason 从理论上讲,回滚是可能的。 但是由于您正在微秒级别获取时间+process.hrtime,因此在这一方面似乎相当准确,虽然不是100%,但足以获得您可以使用的东西,我也是这样做的,没有负面的东西或其他什么,它很好,并且我写了“我并不感到自豪”的解决方案,但它是一个有趣的解决方案。 同时,没有人确切知道幕后发生了什么,如果您深入研究,cpp节点插件可以解决这个问题,请给我们反馈。 - pirs
这不是理论上的。我已经在三个不同的代码库中修复了这个确切的错误。只需要你在第二/分钟/毫秒的“顶部”开始一个任务,然后发生翻转。当你正在测量接近小单位工作时间分辨率的测量值时,比如在一个需要花费微秒来进行对象分配的应用程序上测量纳秒时,它特别容易发生。正如我所说的,当你的应用程序因为计时器错误而崩溃或陷入循环时,这并不是学术性的问题。 - Jason
而且我要补充的是,每次我不得不修复它,因为罪犯声称“那不可能发生”,而事实上已经发生了两次。当我们每秒进行数十亿次计算时,“几乎从不”必须超过一万亿次计算。检查你的数学。 - Jason
@Jason 如果你想要更精确的值,可以使用简单的cpp nodejs插件直接从V8返回时间... 然后就完成了。 (注:抱歉我不会为你做这个) - pirs
如果您使用hrtime API的两个数据片段而不是尝试在两个不兼容且非原子调用之间拆分时间戳,那么将会更简单。hrtime在NTP方面具有不同的保证,而hrtime则没有。您无法在运行时间超过30秒的应用程序上比较或合并来自两者的值。 - Jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接