奇怪的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中显示格式化的用户输入
Oct 09 PHP
一个MYSQL操作类
Nov 16 PHP
php之对抗Web扫描器的脚本技巧
Oct 01 PHP
Zend framework处理一个http请求的流程分析
Feb 08 PHP
PHP实例分享判断客户端是否使用代理服务器及其匿名级别
Jun 04 PHP
ThinkPHP上使用多说评论插件的方法
Oct 31 PHP
php实现用于计算执行时间的类实例
Apr 18 PHP
PHP永久登录、记住我功能实现方法和安全做法
Apr 27 PHP
php可扩展的验证类实例(可对邮件、手机号、URL等验证)
Jul 09 PHP
django中的ajax组件教程详解
Oct 18 PHP
PHP后台备份MySQL数据库的源码实例
Mar 18 PHP
php定期拉取数据对比方法实例
Sep 22 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
为查询结果建立向后/向前按钮
2006/10/09 PHP
destoon实现不同会员组公司名称显示不同的颜色的方法
2014/08/22 PHP
jquery autocomplete自动完成插件的的使用方法
2010/08/07 Javascript
基于jQuery的仿flash的广告轮播
2010/11/05 Javascript
js判断FCKeditor内容是否为空的两种形式
2013/05/14 Javascript
JavaScript实现动态删除列表框值的方法
2015/08/12 Javascript
Eclipse引入jquery报错如何解决
2015/12/01 Javascript
javascript表单控件实例讲解
2016/09/13 Javascript
详解Bootstrap各式各样的按钮(推荐)
2016/12/13 Javascript
jQuery中的siblings()是什么意思(推荐)
2016/12/29 Javascript
jQuery实现三级联动效果
2017/03/02 Javascript
jQuery自定义多选下拉框效果
2017/06/19 jQuery
vue+vux实现移动端文件上传样式
2017/07/28 Javascript
使用D3.js+Vue实现一个简单的柱形图
2018/08/05 Javascript
node爬取新型冠状病毒的疫情实时动态
2020/02/06 Javascript
es6中let和const的使用方法详解
2020/02/24 Javascript
JS+canvas五子棋人机对战实现步骤详解
2020/06/04 Javascript
Python中的is和id用法分析
2015/01/26 Python
Python遍历目录并批量更换文件名和目录名的方法
2016/09/19 Python
Python基本数据结构与用法详解【列表、元组、集合、字典】
2019/03/23 Python
python pytest进阶之xunit fixture详解
2019/06/27 Python
python在openstreetmap地图上绘制路线图的实现
2019/07/11 Python
详解Django配置优化方法
2019/11/18 Python
python模块和包的应用BASE_PATH使用解析
2019/12/14 Python
使用PyCharm安装pytest及requests的问题
2020/07/31 Python
Python3.8安装Pygame教程步骤详解
2020/08/14 Python
python实现计算图形面积
2021/02/22 Python
如何使用amaze ui的分页样式封装一个通用的JS分页控件
2020/08/21 HTML / CSS
Jacadi Paris美国官方网站:法国童装品牌
2017/10/15 全球购物
联想瑞士官方网站:Lenovo Switzerland
2017/11/19 全球购物
新春联欢会主持词
2014/03/24 职场文书
工程质量承诺书范文
2014/03/27 职场文书
仓库规划计划书
2014/04/28 职场文书
创业培训计划书
2014/05/03 职场文书
2014年图书馆个人工作总结
2014/12/18 职场文书
python opencv常用图形绘制方法(线段、矩形、圆形、椭圆、文本)
2021/04/12 Python