NodeJS 性能优化之 CPU 看图篇

服务稳定性到一定程度之后,都会开始经历一段精细化运营的过程,从成本意识角度来说也是成立的。作为前端出身的NodeJS开发者们,产生共鸣的那就是如何能够直观且快速发现性能瓶颈,能够像调试前端的JS代码那样可视化,堆栈化,接下来我们就针对常见的CPU性能分析方法来揭开NodeJS的CPU面纱。

一、CPU使用情况可视化展示(火焰图—Flame Graph)

充分利用劳动工具有助于帮助我们提升定位问题的效率,Linux kernal自带的系统性能分析工具perf,为我们提供函数级与指令级的热点查找,常用于性能瓶颈的查找与热点代码定位。

#NodeJS如何正确完整的采集火焰图呢?

1-1、用例构造

NodeJS服务代码示例—JSON编解码,如下片段:

1-2 启动方式:

启动参数:--perf_basic_prof或—perf-basic-prof适用于node@0.11.13

也可以使用--perf_basic_prof_only_functions

标准方式:node --perf_basic_prof JsonParse.js

MIG tafNodeJS启动方式:私有模板配置启动参数,如下:


<taf>

<application>

    <server>

        node-args=--perf-basic-prof

</server>

</application>

</taf>

注:--perf_basic_prof或--perf-basic-prof会对应生成一个/tmp/perf-pid.map文件,如图:

1-3 perf脚本采集函数和热点代码

#查看NodeJS服务进程pid,采集时需要用到。命令:$ ps –ef | grep ‘worker’

#采集脚本:$ perf record -F 99 -p 182497 -g -- sleep 60

参数说明如下:

采集频率(ms)

进程pid

调用记录

记录时长

-F 99

-p 182497

-g

--sleep 60

#进程对应的符号表perf-pid.map权限设置:$chown root /tmp/perf-pid.map

踩过的坑!!!是为了消除下面这个问题的

File /tmp/perf-PID.map not owned by current user or root, ignoring it (use -f to override).

Failed to open /tmp/perf-PID.map, continuing without symbols

  • 挖出FlameGraph开源库里面的stackcollapse-perf.pl和flamegraph.pl

http://github.com/brendangregg/FlameGraph

  • svg文件生成

单一颜色:$ perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > xxx.svg

多种颜色:perf script | ./stackcollapse-perf.pl | ./flamegraph.pl --color=js –hash > xxx.svg,如下图:

二、CPU火焰图的理解与性能分析

2.1 通过上面的步骤采集出两种不同颜色系的火焰图,如下图

2.2 火焰图颜色对应关系,如下表:

颜色

类型

绿色

JS代码调用

蓝色

优化编译代码

黄色

C++/C代码

红色

libuv系统调用

2.3 火焰图形状对应关系

形状

含义

每一个平面方块

一个函数在栈中的位置(也称一个栈帧)

Y轴

栈的深度(也叫栈的帧数)

X轴

表示总的样例,不过它们左右顺序没有特殊含义

每个平面方块的宽度

方块的宽度标示CPU使用时间或者说相对父函数而言使用CPU的比率,越宽代表占用CPU的时间越长,或者使用CPU很频繁

2.4 JSON序列化与反序列化火焰图分析

采用ab进行压力测试分析JSON.parse与JSON.stringify性能开销

压测命令:ab –n 3000000 -c 50 http://ip:port/jsonParse

从火焰图看到JsonParse.js里面耗时主要消耗在JSON序列化和反序列化

几个常见的栈帧类型说明:

栈帧

含义

LazyCompile

指的是下回会被编译

Builtin

指的是C++内置的运算方法

Stub

C入口桩代码:作用是在js的JIT代码中,如果要调用Runtime的函数,则通过CEntryStub实现

V8::internal

内部命名空间,就是C++的namespace,在V8的源代码可以找到对应的namespace

2.5 JSON反序列化(JSON.parse)源码分析

概念普及——解析器常见原理:

a) 词法分析

b) 语法分析生成抽象语法树(AST)

c) 针对抽象语法树进行语义分析,构建你需要的内部数据结构或生成代码

通过局部查看火焰图分析源码

1 Stub:CEntryStub:C入口桩代码,在JS的JIT代码中,提供调用Runtime的函数(如DOM函数或者JS的builtin函数)

2【编译builtins.h】v8::internal::Builtin_JsonParse

3【解析器json-parser.cc】v8::internal::JsonParser<true>::ParseJson

4【词法分析json-parser.cc】v8::internal::JsonParser<true>::ParseJsonValue

5【语法分析json-parser.cc】v8::internal::JsonParser<true>::ParseJsonObject

小结:通过火焰图我们能够清晰的看到函数的调用栈,并能够找到哪些函数是耗时较多的

JSON序列化流程相似相似,感兴趣的同学可以看一下V8的json-stringifier.cc和对应的.h文件

三、CPU性能分析的另一种可视化dot图

3.1 需要gprof2dot脚本

工程地址:https://github.com/jrfonseca/gprof2dot.git

3.2 基于perf.data文件转换成dot文件

$ perf script -i perf.data.pid | python gprof2dot.py –f perf –e 1 –o xxx.dot

3.3 基于perf.data文件转换成dot文件

$ dot –Tsvg xxx.dot –o xxx.svg

在linux物理机的尝试:

下载到PC磁盘,打开即可:

附录

https://zhuanlan.zhihu.com/p/27147421

https://www.zhihu.com/question/24640264

http://blog.12.rs/notes/V8-0.2.5-JIT/

https://github.com/zfengzhen/Blog/blob/master/article/hold%E4%BD%8F%E4%BD%A0%E7%9A%84%E5%90%8E%E5%8F%B0%E7%A8%8B%E5%BA%8F.md

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

郑清江的专栏

1 篇文章1 人订阅

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏点滴积累

geotrellis使用(十三)数据导入BUG解决方案说明

Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 BUG说明...

3627
来自专栏深度学习那些事儿

提升python项目完成效率的调试方法技巧(上)

效率提升是极为重要的事情,我们的时间本来就不充裕,不应该过多将时间浪费在调试过程中。对于大型项目光有dubug是不够的,如果需要提高产品调试进度,必须需要采取一...

2987
来自专栏皮振伟的专栏

[linux][memory]ksm/uksm的调优和优化尝试

前言: 在前文《[linux][memory]KSM技术分析》中,分析了KSM技术的基本实现原理。这里再总结一下使用ksm/uksm遇到的几个问题,并附加上作者...

45612
来自专栏编舟记

Clojure集合管道函数练习

TDD讨论组里的申导最近在B站直播了Martin Fowler的经典文章Refactoring with Loops and Collection Pipeli...

972
来自专栏Java进阶架构师

dubbo专题-深入分析zookeeper连接原理

上篇的dubbo源码解析-远程暴露中讲解了远程暴露的大致过程,但是期间大家也发现了,这个过程涉及到了很多分支,比如netty,zookeeper.当然设计模式就...

1502
来自专栏落影的专栏

iOS开发笔记(九)— 数据库、Crash、内存问题分析

分享iOS开发中遇到的问题,和相关的一些思考,本次内容包括:UIKit的iOS11问题、数据库问题定位、线上Crash处理、内存问题分析。

1002
来自专栏铭毅天下

Elasticsearch聚合后分页深入详解

1、Elasticsearch支持聚合后分页吗,为什么? 不支持,看看Elasticsearch员工如何解读。 ? ? 这个问题,2014年在gith...

68111
来自专栏owent

给客户端写得LRU缓存

由于我们的客户端的元素和资源比较多,cocos框架的各种库质量参差不齐,导致了有些地方加载速度实在很慢。并且没有一个统一的内存管理机制导致了整个内存占用不太好控...

791
来自专栏Java技术

【面试题】2018年最全Java面试通关秘籍第五套!

注:本文是从众多面试者的面试经验中整理而来,其中不少是本人出的一些题目,网络资源众多,如有雷同,纯属巧合!禁止一切形式的碰瓷行为!未经允许禁止一切形式的转载和复...

821
来自专栏有趣的Python和你

Python数据分析之dataframe的groupbygroupby函数highcharts绘图

773

扫码关注云+社区