程序完成以后,性能和稳定性又是我们关注的焦点,本文总结一下工作中遇到的性能测试方法,当然这里是针对node。
性能分析是比较困难的工作,如果一个程序很慢,他可能把时间花在了CPU,I/O,网络传输,垃圾回收和一些其他事情。如果我们能找到变慢的地方,并加以改进,那么我们的程序将比竞争对手的优秀。
下面我们就看看有哪些方法可以帮助我们找到性能不佳的模块。
日志
开发app的程序员往往不喜欢使用日志,无论是iOS,Android或者hybrid的开发方案,都有完善的调试机制,IDE更是好用的不行,似乎日志只用在服务器端的程序,嵌入式和驱动开发中才用的到。不过这里想介绍一下日志也可以性能调试,所以给自己的模块打上完整的日志,是一种很好的开发方法。
如果要用日志进行性能分析,必须让日志打印出以下几个要素
- 时间
- 功能模块
- 描述
- 进程(线程)Id
我们看一个例子。
在下图中我们可以看到:
- 使用notepad++工具打开log。
- 按照功能模块搜索。
- 在下面们可以看到过滤后的log。这个和grep的功能很像哦。
- 通过一行一行的查看(注意只看你过滤的模块),发现在哪里耗时最多。
- 比如发现某两行之间耗时2秒,超过预期,我们可以去看完整log,发现在此之间做了哪些操作。
- 如果是多进程的系统,也可以根据进程过滤。
使用log的方法需要你可以修改源码,在关心的地方插入log(比如一些生命周期的关键地方,启动结束与显示等等),也需要在团队中推行这种做法,不然log不全,看了也没啥用。
如果log非常全,就需要使用过滤工具去过滤log,grep和nodepad++的过滤功能都很强大。
工具
性能测试的工具很多,总体思想都是图形化调用堆栈,显示占用的事件百分比。
1. Flame Graphs
下面显示的Flame Graphs可以显示出函数占用的时间,应该说是非常好用。
具体的使用方法可以查看这篇博客cpuflamegraphs
2. v8-profiler
var fs = require('fs');
var profiler = require('v8-profiler');
profiler.startProfiling('1', true);
var profile1 = profiler.stopProfiling();
profiler.startProfiling('2', true);
var profile2 = profiler.stopProfiling();
console.log(snapshot1.getHeader(), snapshot2.getHeader());
profile1.export(function(error, result) {
fs.writeFileSync('profile1.json', result);
profile1.delete();
});
profile2.export()
.pipe(fs.createWriteStream('profile2.json'))
.on('finish', function() {
profile2.delete();
});
profiler生成的文件可以用 node-inspector查看
** 3.d8 **
d8工具需要自己编译生成,我们可以看看v8的的代码,弄清楚v8工具以后,我们也可以自己写工具去测试性能,因为实际上v8本身暴露了这些接口。
如果没有d8,node也有类似的功能。
node --prof --prof_lazy app.js
我们看一下最终的结果:
下面可以看到cpu的使用情况,是不是很不错啊。
Statistical profiling result from benchmarks\v8.log, (4192 ticks, 0 unaccounted, 0 excluded).
[Shared libraries]:
ticks total nonlib name
9 0.2% 0.0% C:\WINDOWS\system32\ntdll.dll
2 0.0% 0.0% C:\WINDOWS\system32\kernel32.dll
[JavaScript]:
ticks total nonlib name
741 17.7% 17.7% LazyCompile: am3 crypto.js:108
113 2.7% 2.7% LazyCompile: Scheduler.schedule richards.js:188
103 2.5% 2.5% LazyCompile: rewrite_nboyer earley-boyer.js:3604
103 2.5% 2.5% LazyCompile: TaskControlBlock.run richards.js:324
96 2.3% 2.3% Builtin: JSConstructCall
...
[C++]:
ticks total nonlib name
94 2.2% 2.2% v8::internal::ScavengeVisitor::VisitPointers
33 0.8% 0.8% v8::internal::SweepSpace
32 0.8% 0.8% v8::internal::Heap::MigrateObject
30 0.7% 0.7% v8::internal::Heap::AllocateArgumentsObject
...
[GC]:
ticks total nonlib name
458 10.9%
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 2.0% are not shown.
ticks parent name
741 17.7% LazyCompile: am3 crypto.js:108
449 60.6% LazyCompile: montReduce crypto.js:583
393 87.5% LazyCompile: montSqrTo crypto.js:603
212 53.9% LazyCompile: bnpExp crypto.js:621
212 100.0% LazyCompile: bnModPowInt crypto.js:634
212 100.0% LazyCompile: RSADoPublic crypto.js:1521
181 46.1% LazyCompile: bnModPow crypto.js:1098
181 100.0% LazyCompile: RSADoPrivate crypto.js:1628
性能测试工具很多,有了工具以后还需要我们对需要优化的代码比较熟悉,仔细检查,才能找到有可能的几个点,慢慢优化,可以说是一件很费时间的工作。
自己写测试工具
上面的工具虽然好用,但是如果我们自己能写得化岂不是更好,所以我们看看能不能自己写一个。
我们先看看v8-profiler是如何做的
https://github.com/node-inspector/v8-profiler/blob/master/src/cpu_profiler.cc
v8::CpuProfiler::StartProfiling(title);
好像v8自己有性能分析的模块哦。
我们再打开v8代码。可以看到
所以仿照现有的工具,我们完全可以打造出自己的工具阿。