在开始分析之前,我们得掌握一些与性能分析相关的函数。这些函数让我们对程序性能有更好的分析和评测。
一、性能分析相关的函数与命令
1.1、时间度量函数
平时我们常用 time() 函数,但是返回的是秒数,对于某段代码的内部性能分析,到秒的精度是不够的。于是要用 microtime 函数。而 microtime 函数可以返回两种形式,一是字符串的形式,一是浮点数的形式。不过需要注意的是,在缺省的情况下,返回的精度只有4位小数。为了获得更高的精确度,我们需要配置 precision。
如下是 microtime 的使用结果。
$start= microtime(true);
echo $start."\n";
$end = microtime(true);
echo $end."\n";
echo ($end-$start)."\n";
输出为:
bash-3.2# phptime.php
1441360050.3286
1441360050.3292
0.00053000450134277
而在代码前面加上一行:
ini_set("precision", 16);
输出为:
bash-3.2# phptime.php
1441360210.932628
1441360210.932831
0.0002031326293945312
除了 microtime 内部统计之外, 还可以使用 getrusage 来取得用户态的事长。在实际的操作中,也常用 time 命令来计算整个程序的运行时长,通过多次运行或者修改代码后运行,得到不同的时间长度以得到效率上的区别。 具体用法是:time phptime.php ,则在程序运行完成之后,不管是否正常结束退出,都会有相关的统计。
bash-3.2# time phptime.php
1441360373.150756
1441360373.150959
0.0002031326293945312
real 0m0.186s
user 0m0.072s
sys 0m0.077s
因为本文所讨论的性能问题,往往分析上百万次调用之后的差距与趋势,为了避免代码中存在一些时间统计代码,后面我们使用 time 命令居多。
1.2、内存使用相关函数
分析内存使用的函数有两个:memory_ get_ usage、memory_ get_ peak_usage,前者可以获得程序在调用的时间点,即当前所使用的内存,后者可以获得到目前为止高峰时期所使用的内存。所使用的内存以字节为单位。
$base_memory= memory_get_usage();
echo "Hello,world!\n";
$end_memory= memory_get_usage();
$peak_memory= memory_get_peak_usage();
echo $base_memory,"\t",$end_memory,"\t",($end_memory-$base_memory),"\t", $peak_memory,"\n";
输出如下:
bash-3.2# phphelloworld.php
Hello,world!
224400 224568 168 227424
可以看到,即使程序中间只输出了一句话,再加上变量存储,也消耗了168个字节的内存。
对于同一程序,不同 PHP 版本对内存的使用并不相同,甚至还差别很大。
~~~
$baseMemory= memory_get_usage();
class User
{
private $uid;
function __construct($uid)
{
$this->uid= $uid;
}
}
for($i=0;$i<100000;$i++)
{
$obj= new User($i);
if ( $i% 10000 === 0 )
{
echo sprintf( '%6d: ', $i), memory_get_usage(), " bytes\n";
}
}
echo " peak: ",memory_get_peak_usage(true), " bytes\n";
在 PHP 5.2 中,内存使用如下:
[root@localhostphpperf]# php52 memory.php
0: 93784 bytes
10000: 93784 bytes
…… 80000: 93784 bytes
90000: 93784 bytes
peak: 262144 bytes
PHP 5.3 中,内存使用如下
[root@localhostphpperf]# phpmemory.php
0: 634992 bytes
10000: 634992 bytes
…… 80000: 634992 bytes
90000: 634992 bytes
peak: 786432 bytes
可见 PHP 5.3 在内存使用上要粗放了一些。
PHP 5.4 - 5.6 差不多,有所优化:
~~~
~~~
[root@localhostphpperf]# php56 memory.php
0: 224944 bytes
10000: 224920 bytes
…… 80000: 224920 bytes
90000: 224920 bytes
peak: 262144 bytes
而 PHP 7 在少量使用时,高峰内存的使用,增大很多。
[root@localhostphpperf]# php7 memory.php
0: 353912 bytes
10000: 353912 bytes
…… 80000: 353912 bytes
90000: 353912 bytes
peak: 2097152 bytes
~~~
从上面也看到,以上所使用的 PHP 都有比较好的垃圾回收机制,10万次初始化,并没有随着对象初始化的增多而增加内存的使用。PHP7 的高峰内存使用最多,达到了接近 2M。
下面再来看一个例子,在上面的代码的基础上,我们加上一行,即如下加粗的一行:
~~~
$obj->self = $obj;
代码如下:
$baseMemory= memory_get_usage();
class User
{
private $uid;
function __construct($uid)
{
$this->uid= $uid;
}
}
for($i=0;$i<100000;$i++)
{
$obj= new User($i);
$obj->self = $obj;
if ( $i% 5000 === 0 )
{
echo sprintf( '%6d: ', $i), memory_get_usage(), " bytes\n";
}
}
echo " peak: ",memory_get_peak_usage(true), " bytes\n";
~~~
这时候再来看看内存的使用情况,中间表格主体部分为内存使用量,单位为字节。
图表如下:
PHP 5.2 并没有合适的垃圾回收机制,导致内存使用越来越多。而5.3 以后内存回收机制导致内存稳定在一个区间。而也可以看见 PHP7 内存使用最少。把 PHP 5.2 的图形去掉了之后,对比更为明显。
可见 PHP7 不仅是在算法效率上,有大幅度的提升,在大批量内存使用上也有大幅度的优化(尽管小程序的高峰内存比历史版本所用内存更多)。
1.3、垃圾回收相关函数
在 PHP 中,内存回收是可以控制的,我们可以显式地关闭或者打开垃圾回收,一种方法是通过修改配置,zend.enable_gc=Off 就可以关掉垃圾回收。缺省情况下是 On 的。另外一种手段是通过 gc _enable()和gc _disable()函数分别打开和关闭垃圾回收。
比如在上面的例子的基础上,我们关闭垃圾回收,就可以得到如下数据表格和图表。
代码如下:
~~~
gc_disable();
$baseMemory= memory_get_usage();
class User
{
private $uid;
function __construct($uid)
{
$this->uid= $uid;
}
}
for($i=0;$i<100000;$i++)
{
$obj= new User($i);
$obj->self = $obj;
if ( $i% 5000 === 0 )
{
echo sprintf( '%6d: ', $i), memory_get_usage(), " bytes\n";
}
}
echo " peak: ",memory_get_peak_usage(true), " bytes\n";
~~~
分别在 PHP 5.3、PHP5.4 、PHP5.5、PHP5.6 、PHP7 下运行,得到如下内存使用统计表。
图表如下,PHP7 还是内存使用效率最优的。
从上面的例子也可以看出来,尽管在第一个例子中,PHP7 的高峰内存使用数是最多的,但是当内存使用得多时,PHP7 的内存优化就体现出来了。
这里值得一提的是垃圾回收,尽管会使内存减少,但是会导致速度降低,因为垃圾回收也是需要消耗 CPU 等其他系统资源的。Composer 项目就曾经因为在计算依赖前关闭垃圾回收,带来成倍性能提升,引发广大网友关注。详见:
https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799
在常见的代码和性能分析中,出了以上三类函数之外,还常使用的有堆栈跟踪函数、输出函数,这里不再赘述。
- PHP技术文章
- PHP中session和cookie的区别
- php设计模式(一):简介及创建型模式
- php设计模式结构型模式
- Php设计模式(三):行为型模式
- 十款最出色的 PHP 安全开发库中文详细介绍
- 12个提问频率最高的PHP面试题
- PHP 语言需要避免的 10 大误区
- PHP 死锁问题分析
- 致PHP路上的“年轻人”
- PHP网站常见安全漏洞,及相应防范措施总结
- 各开源框架使用与设计总结(一)
- 数据库的本质、概念及其应用实践(二)
- PHP导出MySQL数据到Excel文件(fputcsv)
- PHP中14种排序算法评测
- 深入理解PHP原理之--echo的实现
- PHP性能分析相关的函数
- PHP 性能分析10则
- 10 位顶级 PHP 大师的开发原则
- 30条爆笑的程序员梗 PHP是最好的语言
- PHP底层的运行机制与原理
- PHP 性能分析与实验——性能的宏观分析
- PHP7 性能翻倍关键大揭露
- 鸟哥:写在PHP7发布之际一些话
- PHP与MySQL通讯那点事
- Php session内部执行流程的再次剖析
- 关于 PHP 中的 Class 的几点个人看法
- PHP Socket 编程过程详解
- PHP过往及现在及变革
- PHP吉祥物大象的由来
- PHP生成静态页面的方法
- 吊炸天的 PHP 7 ,你值得拥有!
- PHP开发中文件操作疑难问答
- MongoDB PHP Driver的连接处理解析
- PHP 杂谈《重构-改善既有代码的设计》之二 对象
- 在php中判断一个请求是ajax请求还是普通请求的方法
- 使用HAProxy、PHP、Redis和MySQL支撑10亿请求每周架构细节
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重构-改善既有代码的设计
- PHP场景中getshell防御思路分享
- 移动互联时代,你看看除了PHP你还会些什么
- 安卓系统上搭建本地php服务器环境
- PHP中常见的缓存技术!
- PHP里10个鲜为人知但却非常有用的函数
- 成为一名PHP专家其实并不难
- PHP 命令行?是的,您可以!
- PHP开发提高效率技巧
- PHP八大安全函数解析
- PHP实现四种基本排序算法
- PHP开发中的中文编码问题
- php.get.post
- php发送get、post请求的6种方法简明总结
- 中高级PHP开发者应该掌握哪些技术?
- 前端开发
- web前端知识体系大全
- 前端工程与性能优化(下)
- 前端工程与性能优化(上)
- 2016 年技术发展方向
- Web应用检查清单
- 如何成为一名优秀的web前端工程师
- 前端组件化开发实践
- 移动端H5页面高清多屏适配方案
- 2015前端框架何去何从
- 从前端看“百度迁徙”的技术实现(一)
- 从前端看“百度迁徙”的技术实现(二)
- 前端路上的旅行
- 大公司里怎样开发和部署前端代码?
- 5个经典的前端面试问题
- 前端工程师新手必读
- 手机淘宝前端的图片相关工作流程梳理
- 一个自动化的前端项目实现(附源码)
- 前端代码异常日志收集与监控
- 15年双11手淘前端技术总结 - H5性能最佳实践
- 深入理解javascript原型和闭包系列
- 一切都是对象
- 函数和对象的关系
- prototype原型
- 隐式原型
- instanceof
- 继承
- 原型的灵活性
- 简述【执行上下文】上
- 简述【执行上下文】下
- this
- 执行上下文栈
- 简介【作用域】
- 【作用域】和【上下文环境】
- 从【自由变量】到【作用域链】
- 闭包
- 完结
- 补充:上下文环境和作用域的关系
- Linux私房菜