奇怪的PHP引用效率问题分析
2015-01-24信息快讯网
最近写了一个小的php程序处理日志中的ip,需要将每个日志中出现的ip都接上一个出现时间戳的链表,于是按行遍历log日志并写了一个update_timequeue的函数来插入时间戳节点
函数如下:function update_timelist(&$arr,$timestamp,$threshold){ $timequeue = &$arr['timequeue']; while(!empty($timequeue[0])&&($timestamp-$timequeue[0])>$threshold){ array_shift($timequeue); } array_push($timequeue, $timestamp); if($arr['count']<count($timequeue)){ $arr['count'] = count($timequeue); } }
大家看出来这个函数有什么问题了没有?其实,有很大一个问题,就是函数中的:
$timequeue = &$arr['timequeue'];
这一行导致程序读入22M数据并生成时间节点链表用了接近40秒,而删掉该行改成直接使用$arr['timequeue']时间就缩短了30秒,只需要10秒左右就处理完了22M。
function update_timelist(&$arr,$timestamp,$threshold){ while(!empty($arr['timequeue'][0])&&($timestamp-$arr['timequeue'][0])>$threshold){ array_shift($arr['timequeue']); } array_push($arr['timequeue'], $timestamp); if($arr['count']<count($arr['timequeue'])){ $arr['count'] = count($arr['timequeue']); }
大家看出来是什么问题了吗?问题就count函数上,没有想到吧。PHP将变量指向的真正的内容空间标记为了引用类型和非引用类型,像下面的代码:
$a = 'jb51.net'; $b = $a; $c = $b;
实际占用内存空间只有一份,因为PHP的zend引擎使用copy on writing的机制,只在$b,$c修改的时候才会复制一份'jb51.net'过来,此时'jb51.net'的内容空间类型为非引用类型,如果改为下面的代码:
$a = 'jb51.net'; $b = $a; $c = &$a;
这个会有什么变化?仍然是一份内存空间存放'jb51.net'吗?不是,因为$c为$a的引用,$a的指向的存储空间需要标记为引用类型,那么必须为$b单独复制一份'jb51.net'才行了,因为$b指向的是非引用类型。
我们可以这样理解,$c现在是$a的引用了,如果$b仍然执行$a的空间那么修改$c将导致$b也修改,所以此时一旦出现引用即使没有写操作也必须复制一份了。也可以这样理解,php对变量指向的内存空间只有非引用和引用两种类型,两种类型不能混合,不能转移。如果什么地方需要改变内存空间的状态则需要copy一份了。
下面就说明为什么多了$timequeue = &$arr['timequeue']会导致count变慢,还记得c函数的调用过程吗?实际我们传入的参数需要copy一份拷贝传入,php也一样,但是由于copy on writing机制使得count在传入非引用类型时是不会真正copy的,但是$timequeue = &$arr['timequeue']将$timequeue的内存空间指定为了引用类型,而count需要非引用类型,这样就导致count需要copy一份$arr['timequeue']了。直接传入$arr['timequeue']为什么没有问题?count当然是用了copy on writing的机制,array_shift和array_push呢?他们是传入的引用啊,不用担心这不是修改了$arr['timequeue']的类型而是真正的传入了$arr['timequeue']的一个别名。
对于PHP我也是刚刚开始学习,上面的分析不一定正确,也不一定全面。大家可以在我的主页发邮件留言与我交流。
PHP可变函数的使用详解
VIM中设置php自动缩进为4个空格的方法详解
修改php.ini不生效问题解决方法(上传大于8M的文件)
与文件上传有关的php配置参数总结
使用PHP计算两个路径的相对路径
深入解析PHP的引用计数机制
解析PHP处理换行符的问题 \r\n
基于PHP导出Excel的小经验 完美解决乱码问题
joomla jce editor 解决上传中文名文件失败问题
php引用返回与取消引用的详解
coreseek 搜索英文的问题详解
php中将字符串转为HTML的实体引用的一个类
PHP数组传递是值传递而非引用传递概念纠正
php引用计数器进行垃圾收集机制介绍
php引用地址改变变量值的问题
php地址引用(php地址引用的效率问题)
PHP遍历数组的几种方法
php遍历数组的方法分享
php中大括号作用介绍
那些年一起学习的PHP(三)
调试一段PHP程序时遇到的三个问题
yii框架中的Url生产问题小结
fgetcvs在linux的问题
114啦源码(114la)不能生成地方房产和地方报刊问题4级页面0字节的解决方法
使ecshop模板中可引用常量的实现方法
PHP错误抑制符(@)导致引用传参失败Bug的分析
PHP中foreach循环中使用引用要注意的地方
php 传值赋值与引用赋值的区别