除了使用xdebug或者xhprof等PHP扩展分析PHP程序之外,我们还可以使用linux的命令对PHP程序进行分析。当然,这样也有一定缺陷,就是只能分析CLI执行的PHP程序。
以下是将要分析的PHP脚本
<?php
$num="1800";
$arr = array();
for($i=0;$i<20000;$i++){
$arr[]= "{$i}";
}
sleep(5);
for($i=0;$i<3000;$i++){
if(in_array($num,$arr)){
continue;
}
}
第一个命令:time
解析:linux下time命令可以获取到一个程序的执行时间,包括程序的实际运行时间(real time),以及程序运行在用户态的时间(user time)和内核态的时间(sys time)。
-bash-4.1# time php test.php
real 0m5.675s #实际时间(real time): 从command命令行开始执行到运行终止的消逝时间;
user 0m0.665s #用户CPU时间(user CPU time): 命令执行完成花费的用户CPU时间,即命令在用户态中执行时间总和;
sys 0m0.007s #系统CPU时间(system CPU time): 命令执行完成花费的系统CPU时间,即命令在核心态中执行时间总和。
可以看到,sleep的5秒并没有占用到user和sys,也就是说,sleep并不占用cpu
第二个命令:strace
解析:strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
执行命令:
strace -ttt -o strace_result php test.php
-ttt 参数表示微秒级输出,以秒了表示时间
-o 参数表示将结果输出到文件中
1443442345.373206 munmap(0x3915200000, 2276448) = 0
1443442345.373250 munmap(0x7f24a0f34000, 2347568) = 0
1443442345.373291 munmap(0x7f24a0bf6000, 3397480) = 0
1443442345.373334 munmap(0x3914600000, 2275920) = 0
1443442345.373374 munmap(0x7f24a07ed000, 2113912) = 0
1443442345.373434 munmap(0x7f24a09f2000, 2109776) = 0
1443442345.373474 munmap(0x3914a00000, 2346240) = 0
1443442345.373516 munmap(0x3917e00000, 2201520) = 0
1443442345.374489 munmap(0x7f24a158a000, 323584) = 0
1443442345.374622 exit_group(0) = ?
输出结果中,每一行都是一个系统调用。
第三个命令:ltrace
解析:ltrace命令用来跟踪进程调用库函数的情况
-bash-4.1# ltrace -c php test.php
^C% time seconds usecs/call calls function
------ ----------- ----------- --------- --------------------
55.32 97.979893 233 419223 strtol
30.04 53.213711 53213711 1 __libc_start_main
9.19 16.272251 305 53287 memcpy
3.17 5.621030 279 20107 __ctype_b_loc
1.74 3.074011 229 13392 malloc
0.12 0.217684 83 2618 strlen
0.12 0.205593 18690 11 dlopen
0.11 0.194501 79 2445 __ctype_tolower_loc
0.06 0.111315 82 1351 strrchr
0.02 0.043957 80 545 calloc
0.02 0.042310 81 521 free
0.02 0.039427 83 475 strcasecmp
0.01 0.025975 79 327 realloc
0.01 0.022132 11066 2 getprotobyname
0.01 0.016168 81 199 memset
0.00 0.006055 82 73 strncasecmp
0.00 0.004099 87 47 strchr
0.00 0.003631 80 45 fileno
上面列出系统函数的调用次数和执行时间,其中发现strtol调用了40w多次。进行优化时,可以考虑从这方向去入手。
strtol是将字符串转化成long型,在代码中,仅有in_array这个函数实现这个操作(由于使用非严格匹配,in_array如果参数可以转化成int类型,会使用int类型进行比较)
简单修改了一下代码:if(in_array($num,$arr,true)){
再次执行:
% time seconds usecs/call calls function
------ ----------- ----------- --------- --------------------
29.58 11.395604 11395604 1 __libc_start_main
26.64 10.263876 192 53293 memcpy
19.79 7.623026 379 20108 __ctype_b_loc
13.17 5.075651 378 13393 malloc
8.12 3.129727 223 14009 free
0.83 0.319670 83 3832 strlen
0.53 0.203854 18532 11 dlopen
0.50 0.194224 79 2445 __ctype_tolower_loc
可以看到程序执行的时间大大缩短了。