奇怪的PHP引用效率问题分析


Posted in PHP onMarch 23, 2012

函数如下:

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 = '3water.com'; 
$b = $a; 
$c = $b;

实际占用内存空间只有一份,因为PHP的zend引擎使用copy on writing的机制,只在$b,$c修改的时候才会复制一份'3water.com'过来,此时'3water.com'的内容空间类型为非引用类型,如果改为下面的代码:
$a = '3water.com'; 
$b = $a; 
$c = &$a;

这个会有什么变化?仍然是一份内存空间存放'3water.com'吗?不是,因为$c为$a的引用,$a的指向的存储空间需要标记为引用类型,那么必须为$b单独复制一份'3water.com'才行了,因为$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 相关文章推荐
PHP 获取MSN好友列表的代码(2009-05-14测试通过)
Sep 09 PHP
深入PHP变量存储的详解
Jun 13 PHP
php_screw 1.5:php加密: 安装与使用详解
Jun 20 PHP
微信公众平台开发关注及取消关注事件的方法
Dec 23 PHP
php中ob_flush函数和flush函数用法分析
Mar 18 PHP
php类的定义与继承用法实例
Jul 07 PHP
PHP实现连接设备、通讯和发送命令的方法
Oct 13 PHP
PHP中list()函数用法实例简析
Jan 08 PHP
PHP简单预防sql注入的方法
Sep 27 PHP
Yii2针对游客、用户防范规则和限制的解决方法分析
Oct 08 PHP
利用php的ob缓存机制实现页面静态化方法
Jul 09 PHP
PHP7.1实现的AES与RSA加密操作示例
Jun 15 PHP
php地址引用(php地址引用的效率问题)
Mar 23 #PHP
PHP遍历数组的几种方法
Mar 22 #PHP
php遍历数组的方法分享
Mar 22 #PHP
php中大括号作用介绍
Mar 22 #PHP
那些年一起学习的PHP(三)
Mar 22 #PHP
那些年一起学习的PHP(二)
Mar 21 #PHP
那些年一起学习的PHP(一)
Mar 21 #PHP
You might like
PHP4引用文件语句的对比
2006/10/09 PHP
Linux下PHP连接Oracle数据库
2014/08/20 PHP
php 反斜杠处理函数addslashes()和stripslashes()实例详解
2016/12/25 PHP
通过JAVAScript实现页面自适应
2007/01/19 Javascript
jquery 必填项判断表单是否为空的方法
2008/09/14 Javascript
JS正则验证邮箱的格式详细介绍
2013/11/19 Javascript
jQuery防止click双击多次提交及传递动态函数或多参数
2014/04/02 Javascript
JQuery中使用Ajax赋值给全局变量失败异常的解决方法
2014/08/18 Javascript
JavaScript中的setMilliseconds()方法使用详解
2015/06/11 Javascript
jQuery插件开发精品教程(让你的jQuery更上一个台阶)
2015/11/07 Javascript
jquery遍历json对象集合详解
2016/05/18 Javascript
利用Vue.js实现checkbox的全选反选效果
2017/01/18 Javascript
利用vue.js插入dom节点的方法
2017/03/15 Javascript
React Native 集成jpush-react-native的示例代码
2017/08/16 Javascript
JS Testing Properties 判断属性是否在对象里的方法
2017/10/01 Javascript
详解vue中使用express+fetch获取本地json文件
2017/10/10 Javascript
vue做网页开场视频的实例代码
2017/10/20 Javascript
laydate时间日历插件使用方法详解
2018/11/14 Javascript
[36:20]KG vs SECRET 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
12步教你理解Python装饰器
2016/02/25 Python
Pipenv一键搭建python虚拟环境的方法
2018/05/22 Python
Tensorflow卷积神经网络实例
2018/05/24 Python
python 用下标截取字符串的实例
2018/12/25 Python
解决python 文本过滤和清理问题
2019/08/28 Python
python中的Elasticsearch操作汇总
2019/10/30 Python
python cv2.resize函数high和width注意事项说明
2020/07/05 Python
GEOX鞋美国官方网站:意大利会呼吸的鞋
2017/07/12 全球购物
中国跨境在线时尚零售商:Bellelily
2018/04/06 全球购物
马来西亚网上花店:FlowerAdvisor马来西亚
2020/01/03 全球购物
XML文档面试题
2015/08/05 面试题
商务专员岗位职责
2013/11/23 职场文书
党的群众路线教育实践活动心得体会
2014/03/03 职场文书
孙振耀退休感言
2015/08/01 职场文书
干货:企业内部人才推荐奖励方案!
2019/07/09 职场文书
浅谈Python数学建模之固定费用问题
2021/06/23 Python
python_tkinter事件类型详情
2022/03/20 Python