奇怪的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中的类-什么叫类
Nov 20 PHP
php 中文字符入库或显示乱码问题的解决方法
Apr 12 PHP
利用PHP生成静态HTML文档的原理
Oct 29 PHP
让CodeIgniter数据库缓存自动过期的处理的方法
Jun 12 PHP
php使用Imagick生成图片的方法
Jul 31 PHP
php读取torrent种子文件内容的方法(测试可用)
May 03 PHP
解决出现SoapFault (looks like we got no XML document)的问题
Jun 24 PHP
php实现微信企业号支付个人的方法详解
Jul 26 PHP
PHP单例模式与工厂模式详解
Aug 29 PHP
php数据库的增删改查 php与javascript之间的交互
Aug 31 PHP
PHP生成随机数的方法总结
Mar 01 PHP
laravel实现一个上传图片的接口,并建立软链接,访问图片的方法
Oct 12 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三元运算符的结合性介绍
2012/01/10 PHP
PHP的基本常识小结
2013/07/05 PHP
ECSHOP完美解决Deprecated: preg_replace()报错的问题
2016/05/17 PHP
利用PHP将图片转换成base64编码的实现方法
2016/09/13 PHP
ThinkPHP的SAE开发相关注意事项详解
2016/10/09 PHP
ThinkPHP打水印及设置水印位置的方法
2016/10/14 PHP
使用正则去除php代码中的注释方法
2016/11/03 PHP
php简单构造json多维数组的方法示例
2017/06/08 PHP
laravel框架模型中非静态方法也能静态调用的原理分析
2019/11/23 PHP
基于jQuery试卷自动排版系统
2010/07/18 Javascript
JS 控制小数位数的实现代码
2011/08/02 Javascript
JS实现静止元素自动移动示例
2014/04/14 Javascript
简单谈谈node.js 版本控制 nvm和 n
2015/10/15 Javascript
jQuery纵向导航菜单效果实现方法
2016/12/19 Javascript
使用JS模拟锚点跳转的实例
2018/02/01 Javascript
Vuex实现数据增加和删除功能
2019/11/11 Javascript
Vue在H5 项目中使用融云进行实时个人单聊通讯
2020/12/14 Vue.js
Vue 3自定义指令开发的相关总结
2021/01/29 Vue.js
[43:57]LGD vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python执行使用shell命令方法分享
2017/11/08 Python
浅谈python实现Google翻译PDF,解决换行的问题
2018/11/28 Python
Python列表与元组的异同详解
2019/07/02 Python
对tensorflow中的strides参数使用详解
2020/01/04 Python
Python 生成VOC格式的标签实例
2020/03/10 Python
Python ATM功能实现代码实例
2020/03/19 Python
CSS3动画特效在活动页中的应用
2020/01/21 HTML / CSS
HTML5单页面手势滑屏切换原理分析
2017/07/10 HTML / CSS
美国在线精品家居网站:Burke Decor
2017/04/12 全球购物
团代会主持词
2014/04/02 职场文书
家长会学生演讲稿
2014/04/26 职场文书
住房租房协议书
2014/08/20 职场文书
房屋出租委托书格式
2014/09/23 职场文书
物业接待员岗位职责
2015/04/15 职场文书
社区服务活动感想
2015/08/11 职场文书
Nginx如何配置Http、Https、WS、WSS的方法步骤
2021/05/11 Servers
新手初学Java网络编程
2021/07/07 Java/Android