------静态(开发阶段)测试
gprof
gprof是GNU工具之一,它在编译的时候在每个函数的出入口加入了profiling的代码,运行时统计程序在用户态的执行信息,可以得到每个函数的调用次数、执行时间、调用关系等信息,简单易懂。适合于查找用户级程序的性能瓶颈,对于很多时间都在内核态执行的程序,gprof不适合
gprof默认只统计用户函数的信息,如果要统计库函数,应链接libc_p.a库,才可以产生库函数的profiling信息,如下:
gcc -Wall -g -pg -lc_p example.c -o example
oprofile
oprofile也是一个开源的profiling工具,它使用硬件调试寄存器来统计信息,进行 profiling的开销比较小,而且可以对内核进行profiling。它统计的信息非常的多,可以得到cache的缺失率,memory的访存信息, 分支预测错误率等等,这些信息gprof是得不到的,但是对于函数调用次数,它是不能够得到的。
具体参见:http://blog.csdn.net/guogaofeng1219/article/details/5971149
------运行时测试
下面的命令会释放pagecache, dentries and inodes
sync;echo 3 > /proc/sys/vm/drop_caches
这可以消除已加载cache对程序性能的影响
磁盘速度测试dd
在使用前首先了解两个特殊设备
/dev/null 伪设备,回收站.写该文件不会产生IO
/dev/zero 伪设备,会产生空字符流,对它不会产生IO
例如测试磁盘写速度
time dd if=/dev/zero of=/test.dbf bs=8k count=300000
测试磁盘读速度
time dd if=/dev/mapper/VolGroup00-LogVol00 of=/dev/null bs=8k
ifstat
监控网卡传输速度
eth0
KB/s in KB/s out
0.18 0.39
0.12 0.37
0.12 0.25
iostat
显示设备(磁盘)和CPU使用状态;-k某些使用block为单位的列强制使用Kilobytes为单位;1 10表示,数据显示每隔1秒刷新一次,共显示10次。
$: iostat -k 1 10
avg-cpu: %user %nice %system %iowait %steal %idle
0.50 0.00 13.57 0.00 0.00 85.93
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda 0.00 0.00 0.00 0 0
hdc 0.00 0.00 0.00 0 0
kernel将cpu归为3个状态:idle,iowait,busy;idle是真正的idle,没活可干,没有进程是runnale的,cpu空闲;iowait时cpu没法干活(需要的数据还没就绪呢,所以叫wait),这时候cpu也空闲;busy就是在干活。idle和iowait时虽然cpu都是空闲的,但是将这两个状态区分开是很有意义的,因为iowait可以衡量由io问题导致的cpu空闲。
------查看进程使用的资源
/proc/<pid>
例如fd目录:打开的文件(包括socket)
smaps: 进程虚拟内存分布
netstat
进程监听端口、连接信息(对端与本端端口)、进程名等
lsof
-c<进程名> 列出指定进程所打开的文件
-u 列出UID号进程详情
-i<条件> 列出符合条件的进程。(4、6、协议、:端口、 @ip )
-p pid
列出指定进程号所打开的文件
filename 显示打开指定文件的进程信息
lsof -i TCP:9999
a.out 15747 root 7u IPv4 515314 TCP localhost:distinct->localhost:41471 (ESTABLISHED)
a.out 15747 root 8u IPv4 515316 TCP localhost:distinct->localhost:41472 (ESTABLISHED)
其中515314是进程的文件描述符,而41471是对端端口号,这结合了/proc/<pid>/fd与netstat的信息
------查看进程运行位置
pstack
能查看进程的线程栈,但只支持32位
strace
stace -p <pid> 查看进程当前调用的库函数(包括参数值)
------实际运用
简单来说,我们看iostat中%user的比例,如果比较高,一般是程序在进行较复杂的计算,比如排序,如果%user低,而%system高,说明大部分时间花在io上,再结合磁盘利用百分比和%wait来看,如果他们也比较高,说明的确有高速的io访问,或者磁盘或者网卡本身出现了瓶颈,导致io较慢,如果这个百分比比较低,那就是进程对io函数的使用不当,比如read/write较小,或者使用select/poll去轮询大量非活动连接
如果iostat中%user %system都比较低,而程序是个网络连接程序,那可能是到对方连接速度过慢,需要看ping中的返回时间来确定
测试两条命令:
time dd if=/dev/zero of=/test.dbf bs=32 count=10000000
avg-cpu: %user %nice %system %iowait %steal %idle
1.02 0.00
48.47 0.00 0.00 50.51
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00
hdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
time dd if=/dev/zero of=/test.dbf2 bs=8k count=300000
avg-cpu: %user %nice %system %iowait %steal %idle
0.58 0.00
64.53 2.91 0.00 31.98
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
sda 0.00 24522.77 0.00 219.80 0.00 98974.26 900.58 20.99 95.50 1.86
40.79
hdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
两条dd命令运行都运行iostat检测cpu与磁盘使用百分比
发现cpu的利用率都是很高,但是第2条命令运行时,磁盘的利用率比较高,并且时常有一定的iowait(第一条几乎为0)
这正好印证了read/write的效率,当粒度较小时,大部分的时间花在了从系统cache与进程buffer间的传输上,而磁盘其实是比较清闲的,反之,则磁盘比较繁忙,会带来一定的iowait,但是cpu在io可用时,也要频繁的数据传输,带来较高的%system,如果磁盘出现问题,io比较慢,就会%system比较低,%wait或者%idle比较高
测试一个echo server(poll实现) 和一个echo client的例子
客户端到服务端有1000个连接,200个活动,每次全部活动连接试图发送32字节,然后接收会32字节的应答,读写使用readn/writen封装,客户端每秒发送/接收各10M
avg-cpu: %user %nice %system %iowait %steal %idle
0.51 0.00
41.84 0.00 0.00 57.65
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00
hdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
测试发现iostat时cpu使用率比较高(%system部分,说明并非是因为程序计算复杂),ifstat检测网卡传输速率也停留在较低的水平,strace发现进程大部分的调用是在read/write
然后查看gprof的统计结果,也定位在readn/writen部分,说明要改善readn/writen粒度
如果将单次收发字节数改为4096,启动10个这样的客户端,服务端就有10000个连接,2000个活动,客户端发送接收量不变,测试发现,cpu使用率比较高(%system部分),ifstat检测网卡传输速度仍然很低,而gprof的统计结果中,readn/writen的百分比比较低(上一种测试这个很高),strace发现主要的调用时readn/writen/poll,所以判断poll的机制造成了比较高的cpu占用率
虽然,我们都知道小粒度的read/write和poll都比较慢,但是对于一个不清楚代码的进程、或者一个比较大型的项目,而原有的读写部分代码又太多、太分散,我们几乎不可能重构全部不合理代码的时候,采用以上办法都能帮我们快速定位瓶颈所在