奇怪的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 相关文章推荐
生成缩略图
Oct 09 PHP
php数组总结篇(一)
Sep 30 PHP
php 无法载入mysql扩展
Mar 12 PHP
第五章 php数组操作
Dec 30 PHP
检查php文件中是否含有bom的函数
May 31 PHP
php使用sql数据库 获取字段问题介绍
Aug 12 PHP
PHP高手需要要掌握的知识点
Aug 21 PHP
深入浅析php json 格式控制
Dec 24 PHP
ThinkPHP发送邮件示例代码
Oct 08 PHP
php遍历、读取文件夹中图片并分页显示图片的方法
Nov 15 PHP
Laravel 的数据库迁移的方法
Jul 31 PHP
Laravel中获取路由参数Route Parameters的五种方法示例
Sep 29 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数组函数序列之each() - 获取数组当前内部指针所指向元素的键名和键值,并将指针移到下一位
2011/10/31 PHP
一个比较不错的PHP日历类分享
2014/11/18 PHP
PHP数组与对象之间使用递归实现转换的方法
2015/06/24 PHP
php上传图片生成缩略图(GD库)
2016/01/06 PHP
YII2框架中使用yii.js实现的post请求
2017/04/09 PHP
php中pcntl_fork创建子进程的方法实例
2019/03/14 PHP
js左侧多级菜单动态的解决方案
2010/02/01 Javascript
jquery序列化form表单使用ajax提交后处理返回的json数据
2014/03/03 Javascript
node.js中的socket.io的广播消息
2014/12/15 Javascript
jQuery中wrapInner()方法用法实例
2015/01/16 Javascript
js动态切换图片的方法
2015/01/20 Javascript
javascript中var的重要性分析
2015/02/11 Javascript
JavaScript从数组的indexOf()深入之Object的Property机制
2016/05/11 Javascript
JS 对象(Object)和字符串(String)互转方法
2016/05/20 Javascript
谈谈PHP中相对路径的问题与绝对路径的使用
2016/08/16 Javascript
JavaScript实现格式化字符串函数String.format
2016/12/16 Javascript
Angular中sweetalert弹框的基本使用教程
2018/07/22 Javascript
js实现简单选项卡功能
2020/03/23 Javascript
Vue中通过Vue.extend动态创建实例的方法
2019/08/13 Javascript
解决vue打包后刷新页面报错:Unexpected token
2019/08/27 Javascript
Js参数RSA加密传输之jsencrypt.js的使用
2020/02/07 Javascript
python 生成不重复的随机数的代码
2011/05/15 Python
Python使用poplib模块和smtplib模块收发电子邮件的教程
2016/07/02 Python
Python3.5基础之NumPy模块的使用图文与实例详解
2019/04/24 Python
Python+OpenCV实现实时眼动追踪的示例代码
2019/11/11 Python
解决pytorch 保存模型遇到的问题
2021/03/03 Python
HTML5中form如何关闭自动完成功能的方法
2018/07/02 HTML / CSS
美国最顶级的精品店之一:Hampden Clothing
2016/12/22 全球购物
HQhair美国/加拿大:英国化妆品、美容及美发产品商城
2019/04/15 全球购物
建筑专业自我鉴定
2013/10/22 职场文书
护理专业大学生自我推荐信
2014/01/25 职场文书
体育活动总结范文
2014/05/04 职场文书
保护环境倡议书300字
2014/05/19 职场文书
2014班子“三严三实”对照检查材料思想汇报
2014/09/18 职场文书
2015年端午节活动方案
2015/05/05 职场文书
运动会通讯稿600字
2015/07/20 职场文书