Node.js 性能钩子-Performance Timing API

v8.5.0版本起,Node.js 新了性能指标收集的API-Performance Timing API,用于应用性能测试及优化。


  1. Performance Timing API
  2. Class: Performance
  3. Class: PerformanceEntry
  4. Class: PerformanceNodeTiming extends PerformanceEntry
  5. Class: PerformanceObserver
  6. Class: PerformanceObserverEntryList
  7. 示例

1. Performance Timing API

Performance Timing API提供了W3C Performance Timeline规范的实现,目的是支持高分辨率的性能指标收集。这与现代Web浏览器中实现的Performance API相同。

const { PerformanceObserver, performance } = require('perf_hooks');

const obs = new PerformanceObserver((items) => {
  console.log(items.getEntries()[0].duration);
  performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });

performance.mark('A');
doSomeLongRunningProcess(() => {
  performance.mark('B');
  performance.measure('A to B', 'A', 'B');
});


2. Class: Performance

2.1 performance.clearMarks([name])

  • name <string>

如果未提供name,则从Performance Timeline中删除所有PerformanceMark对象。如果提供了name,则仅删除指定名称的标记。


2.2 performance.mark([name])

  • name <string>

在Performance Timeline中创建一个新的PerformanceMark入口点。PerformanceMarkPerformanceEntry的子类,其performanceEntry.entryType始终为'mark'performanceEntry.duration始终是0。性能标记用于标记Performance Timeline中的特定时刻。


2.3 performance.measure(name, startMark, endMark)

  • name <string>
  • startMark <string>
  • endMark <string>

在Performance Timeline中创建一个新的PerformanceMeasure入口点。PerformanceMeasurePerformanceEntry的子类,其performanceEntry.entryType始终是'measure'performanceEntry.duration是自startMarkendMark以来经过的毫秒数。

startMark参数可以标识Performance Timeline中的任何现有PerformanceMark,或者可以标识PerformanceNodeTiming类提供的任何时间戳属性。如果指定名的startMark不存在,则默认startMark设置为timeOrigin

endMark参数必须标识Performance Timeline中的任何现有PerformanceMarkPerformanceNodeTiming类提供的任何时间戳属性。如果指定的endMark不存在,则会引发错误。


2.4 performance.nodeTiming

PerformanceNodeTiming类的一个实例,它为指定的Node.js操作点提供性能指标。


2.5 performance.now()

  • 返回:<number>

返回当前的高分辨率毫秒时间戳,其中0表示当前'node'进程的开始。


2.6 performance.timeOrigin

  • <number>

timeOrigin规范的当前节点进程开始的高分辨率毫秒时间戳,以Unix时间为单位。


2.7 performance.timerify(fn)

  • fn <Function>

在新函数中包含一个函数,用于测量包装函数的运行时间。PerformanceObserver必须订阅'function'事件才能访问时序详细信息。

const {
  performance,
  PerformanceObserver
} = require('perf_hooks');

function someFunction() {
  console.log('hello world');
}

const wrapped = performance.timerify(someFunction);

const obs = new PerformanceObserver((list) => {
  console.log(list.getEntries()[0].duration);
  obs.disconnect();
});
obs.observe({ entryTypes: ['function'] });

// A performance timeline entry will be created
wrapped();


3. Class: PerformanceEntry

3.1 performanceEntry.duration

  • <number>

此入口点经过的总毫秒数。对于所有Performance Entry类型,此值都没有意义。


3.2 performanceEntry.name

  • <string>

性能入口点的名称。


3.3 performanceEntry.startTime

  • <number>

标记Performance Entry开始时间的高分辨率毫秒时间戳。


3.4 performanceEntry.entryType

  • <string>

性能入口点类型。当前可能是以下值之一:'node', 'mark', 'measure', 'gc', 'function''http2'


3.5 performanceEntry.kind

  • <number>

performanceEntry.entryType等于'gc'时,performance.kind属性标识发生的垃圾收集操作的类型。该值可能是以下之一:

  • perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR
  • perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR
  • perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL
  • perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB


4. Class: PerformanceNodeTiming extends PerformanceEntry

PerformanceNodeTiming类是对PerformanceEntry的扩展类,提供Node.js本身的时序详细信息。

4.1 performanceNodeTiming.bootstrapComplete

  • <number>

Node.js进程完成引导的高分辨率毫秒时间戳。如果引导尚未完成,则该属性的值为-1


4.2 performanceNodeTiming.loopExit

  • <number>

Node.js事件循环退出的高分辨率毫秒时间戳。如果事件循环尚未退出,则该属性的值为又-1。它只能在Process'exit'事件的处理程序中有-1值。


4.3 performanceNodeTiming.loopStart

  • <number>

Node.js事件循环开始的高分辨率毫秒时间戳。如果事件循环尚未开始(如,在主脚本的第一个时钟中),则该属性的值为-1


4.4 performanceNodeTiming.nodeStart

  • <number>

初始化Node.js进程的高分辨率毫秒时间戳。


4.5 performanceNodeTiming.v8Start

  • <number>

V8平台初始化的高分辨率毫秒时间戳。


5. Class: PerformanceObserver

5.1 new PerformanceObserver(callback)

PerformanceObserver对象在将新的PerformanceEntry实例添加到Performance Timeline时提供通知。

const {
  performance,
  PerformanceObserver
} = require('perf_hooks');

const obs = new PerformanceObserver((list, observer) => {
  console.log(list.getEntries());
  observer.disconnect();
});
obs.observe({ entryTypes: ['mark'], buffered: true });

performance.mark('test');

因为PerformanceObserver的实例引入本身会有额外性能开销,所以实例不应无限期地订阅通知。用户应在不再需要时立即断开连接。

PerformanceObserver通知新的PerformanceEntry实例时,将调用回调。回调接收PerformanceObserverEntryList实例和对PerformanceObserver的引用。


5.2 performanceObserver.disconnect()

断开PerformanceObserver实例与所有通知的连接。


5.3 performanceObserver.observe(options)

  • options <object>
    • entryTypes <string[]>字符串数组,用于标识观察者感兴趣的PerformanceEntry实例的类型。如果未提供,将引发错误。
    • buffered <boolean>如果为true,则将使用setImmediate()调用通知回调,并在内部缓冲多个PerformanceEntry实例通知。如果为false,则通知将立即且同步。默认值:false

PerformanceObserver实例订阅到options.entryTypes标识的新PerformanceEntry实例的通知。

options.bufferedfalse时,将为每个PerformanceEntry实例调用一次回调:

const {
  performance,
  PerformanceObserver
} = require('perf_hooks');

const obs = new PerformanceObserver((list, observer) => {
  // called three times synchronously. list contains one item
});
obs.observe({ entryTypes: ['mark'] });

for (let n = 0; n < 3; n++)
  performance.mark(`test${n}`);
const {
  performance,
  PerformanceObserver
} = require('perf_hooks');

const obs = new PerformanceObserver((list, observer) => {
  // called once. list contains three items
});
obs.observe({ entryTypes: ['mark'], buffered: true });

for (let n = 0; n < 3; n++)
  performance.mark(`test${n}`);


6. Class: PerformanceObserverEntryList

PerformanceObserverEntryList类用于提供对传递给PerformanceObserverPerformanceEntry实例的访问。


6.1 performanceObserverEntryList.getEntries()

performanceEntry.startTime的形式按时间顺序返回PerformanceEntry对象的列表。


6.2 performanceObserverEntryList.getEntriesByName(name[, type])

返回PerformanceEntry对象的列表,这些对象按时间顺序依次为performanceEntry.startTime,其performanceEntry.name等于name,并且可选,其performanceEntry.entryType等于type


6.3 performanceObserverEntryList.getEntriesByType(type)

返回PerformanceEntry对象的列表,该列表按时间顺序相当于performanceEntry.entryType等于typeperformanceEntry.startTime


7. 示例

测量异步操作的持续时间

以下示例使用Async Hooks和Performance API来测量Timeout操作的实际持续时间(包括执行回调的时间):

'use strict';
const async_hooks = require('async_hooks');
const {
  performance,
  PerformanceObserver
} = require('perf_hooks');

const set = new Set();
const hook = async_hooks.createHook({
  init(id, type) {
    if (type === 'Timeout') {
      performance.mark(`Timeout-${id}-Init`);
      set.add(id);
    }
  },
  destroy(id) {
    if (set.has(id)) {
      set.delete(id);
      performance.mark(`Timeout-${id}-Destroy`);
      performance.measure(`Timeout-${id}`,
                          `Timeout-${id}-Init`,
                          `Timeout-${id}-Destroy`);
    }
  }
});
hook.enable();

const obs = new PerformanceObserver((list, observer) => {
  console.log(list.getEntries()[0]);
  performance.clearMarks();
  observer.disconnect();
});
obs.observe({ entryTypes: ['measure'], buffered: true });

setTimeout(() => {}, 1000);


测量加载依赖项所需的时间

以下示例测量require()操作加载依赖项的持续时间:

'use strict';
const {
  performance,
  PerformanceObserver
} = require('perf_hooks');
const mod = require('module');

// Monkey patch the require function
mod.Module.prototype.require =
  performance.timerify(mod.Module.prototype.require);
require = performance.timerify(require);

// Activate the observer
const obs = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach((entry) => {
    console.log(`require('${entry[0]}')`, entry.duration);
  });
  obs.disconnect();
});
obs.observe({ entryTypes: ['function'], buffered: true });

require('some-module');