在 Node.js 中,可以通过使用 Performance API 来对 require
模块的性能进行检测。这里的 Performance 模块,是 Node.js 根据 W3C Performance Timeline 规范,实现的一套和 Web 相同的 API 接口集合。一般的时间测量,可以通过 Performance.mark
和 Performance.measure
的组合来进行,使用的方法大体上和 Web 中一致(但是需要使用 PerformanceObserver
来获取测量的结果,这一点和 Web 不太相同,具体可以参考官方的文档)。
和 Web 不同的是,在 Node.js 的 Performance 模块中,还提供了一个 timerify 的接口,可以简便的对一个函数进行封装,从而测量出这个函数的实际调用时间。
有了这个接口,就可以很容易的测量 Node.js 中加载模块的耗时了。示例代码如下:
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const fs = require('fs');
const mod = require('module');
mod.Module.prototype.require =
performance.timerify(mod.Module.prototype.require);
const obs = new PerformanceObserver((list) => {
const entries = list.getEntries();
fs.writeFileSync('./profile.json', JSON.stringify(entries), 'utf8');
obs.disconnect();
});
obs.observe({
entryTypes: ['function'],
name: 'Module.require',
buffered: true,
});
require('some-path');
这里有几点可以说明一下:
mod.Module.prototype.require
被timerify
之后,所有模块在require
的时候,都会使用被timerify
过的版本;- PerformanceObserver 的作用是获取 entries 的结果;
obs.disconnect
用于解除连接,不再进行后续的接听;obs.observer
设置entryTypes: ['function']
,确保这里timerify
的结果都可以被获取到;obs.observer
中设置buffered: true
,确保 observer 的回调函数不会被立刻执行,而是用setImmediate
延迟调用。这样的好处是,一次require
后,该模块的调用时间和该模块内部调用子模块的耗时都会一次性通过回调函数返回。(注:默认这里的值是false
,见文档);- 官方给出的示例,还
timerify
了require
函数(见这里),这样做会导致当前模块中require
的调用,生成两份 Performance 数据(一份来自require
,一份来自Module.require
)。出于精简的考虑,上面的示例代码中去掉了对require
函数的timerify
。
在上面的示例代码中,最终得到的结果,存放在了一个 JSON 文件内,大体的格式如下:
[
{
"0": "required-module-name",
"name": "Module.require",
"entryType": "function",
"startTime": 7397.399892,
"duration": 112.681678
}
]
这里,0
表示第一个参数的值,对于 require
来说就是具体引用的模块的名称/地址;name
表示是哪个函数的调用,在示例中就是被 timerify
过的 Module.require
函数;entryType
是固定的 function
,因为这个值是通过 timerify
拿到的;startTime
和 duration
分别表示调用开始的时间以及实际调用的耗时。