奇怪的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 取得瑞年与平年的天数的代码
Aug 10 PHP
PHP禁止页面缓存的代码
Oct 23 PHP
php学习之function的用法
Jul 14 PHP
php源代码安装常见错误与解决办法分享
May 28 PHP
php利用腾讯ip分享计划获取地理位置示例分享
Jan 20 PHP
destoon数据库表说明汇总
Jul 15 PHP
PHP获取当前所在目录位置的方法
Nov 26 PHP
php遍历CSV类实例
Apr 14 PHP
yii权限控制的方法(三种方法)
Dec 28 PHP
PHP 二维关联数组根据其中一个字段排序(推荐)
Apr 04 PHP
PHP实现微信提现(企业付款到零钱)
Aug 01 PHP
php去除deprecated的实例方法
Nov 17 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
php模板原理讲解
2013/11/13 PHP
Adnroid 微信内置浏览器清除缓存
2016/07/11 PHP
PHP利用pdo_odbc实现连接数据库示例【基于ThinkPHP5.1搭建的项目】
2019/05/13 PHP
js函数调用常用方法详解
2012/12/03 Javascript
使用JavaScript获取电池状态的方法
2014/05/03 Javascript
浅谈Javascript中的Function与Object
2015/01/26 Javascript
JavaScript实现SHA-1加密算法的方法
2015/03/11 Javascript
jQuery简单实现QQ空间点赞已经取消点赞
2015/04/02 Javascript
浅谈Javascript的静态属性和原型属性
2015/05/07 Javascript
jQuery+HTML5美女瀑布流布局实现方法
2015/09/21 Javascript
JS实现滑动菜单效果代码(包括Tab,选项卡,横向等效果)
2015/09/24 Javascript
基于Bootstrap实现图片轮播效果
2016/05/22 Javascript
echarts学习笔记之图表自适应问题详解
2017/11/22 Javascript
JS实现的透明度渐变动画效果示例
2018/04/28 Javascript
解决vue中使用Axios调用接口时出现的ie数据处理问题
2018/08/13 Javascript
javscript 数组扁平化的实现
2020/02/03 Javascript
vue中实现动态生成二维码的方法
2020/02/21 Javascript
[02:44]2014DOTA2 国际邀请赛中国区预选赛 大神红毯秀
2014/05/25 DOTA
[04:44]显微镜下的DOTA2第二期——你所没有注意到的细节
2014/06/20 DOTA
python爬取网站数据保存使用的方法
2013/11/20 Python
python直接访问私有属性的简单方法
2016/07/25 Python
Python实现的科学计算器功能示例
2017/08/04 Python
python基础练习之几个简单的游戏
2017/11/10 Python
python用BeautifulSoup库简单爬虫实例分析
2018/07/30 Python
TensorFlow 多元函数的极值实例
2020/02/10 Python
Python闭包及装饰器运行原理解析
2020/06/17 Python
python中setuptools的作用是什么
2020/06/19 Python
Python使用itcaht库实现微信自动收发消息功能
2020/07/13 Python
python反编译教程之2048小游戏实例
2021/03/03 Python
纯CSS打造(无图像无js)的非常流行的讲话(语音)气泡效果
2012/12/28 HTML / CSS
Marc Jacobs彩妆官网:Marc Jacobs Beauty
2017/07/03 全球购物
解释一下Windows的消息机制
2014/01/30 面试题
物控部经理职务说明书
2014/02/25 职场文书
西双版纳导游词
2015/02/03 职场文书
JavaScript实现贪吃蛇游戏
2021/06/16 Javascript
python周期任务调度工具Schedule使用详解
2021/11/23 Python