奇怪的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
php读取msn上的用户信息类
Dec 05 PHP
php的array_multisort()使用方法介绍
May 16 PHP
php读取本地文件常用函数(fopen与file_get_contents)
Sep 09 PHP
ThinkPHP 3.2 数据分页代码分享
Oct 14 PHP
php内存缓存实现方法
Jan 24 PHP
php返回字符串中所有单词的方法
Mar 09 PHP
PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法
Feb 15 PHP
PHP使用Redis替代文件存储Session的方法
Feb 15 PHP
PHP实现上传多图即时显示与即时删除的方法
May 09 PHP
老生常谈PHP面向对象之标识映射
Jun 21 PHP
PHP基于进程控制函数实现多线程
Dec 09 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+mysqli批量查询多张表数据的方法
2015/01/29 PHP
php 删除指定文件夹的实例讲解
2017/07/25 PHP
JavaScript实现Sleep函数的代码
2007/03/04 Javascript
【消息提示组件】,兼容IE6/7&amp;&amp;FF2
2007/09/04 Javascript
jQuery技巧大放送 学习jquery的朋友可以看下
2009/10/14 Javascript
JS 修改URL参数(实现代码)
2013/07/08 Javascript
javascript在子页面中函数无法调试问题解决方法
2014/01/17 Javascript
js加入收藏以及使用Jquery更改透明度
2014/01/26 Javascript
JS编写函数实现对身份证号码最后一位的验证功能
2016/12/29 Javascript
JS对象创建的几种方式整理
2017/02/28 Javascript
javascript基本数据类型和转换
2017/03/17 Javascript
selenium 与 chrome 进行qq登录并发邮件操作实例详解
2017/04/06 Javascript
详解Vue用axios发送post请求自动set cookie
2017/05/10 Javascript
老生常谈js中的MVC
2017/07/25 Javascript
vue.js的手脚架vue-cli项目搭建的步骤
2017/08/30 Javascript
Vue核心概念Action的总结
2019/01/18 Javascript
Vue通过for循环随机生成不同的颜色或随机数的实例
2019/11/09 Javascript
Python实现在线程里运行scrapy的方法
2015/04/07 Python
Numpy array数据的增、删、改、查实例
2018/06/04 Python
Python多继承原理与用法示例
2018/08/23 Python
Python 中Django验证码功能的实现代码
2019/06/20 Python
python爬虫项目设置一个中断重连的程序的实现
2019/07/26 Python
Python分类测试代码实例汇总
2020/07/23 Python
详解pycharm2020.1.1专业版安装指南(推荐)
2020/08/07 Python
Pycharm中如何关掉python console
2020/10/27 Python
CSS3中伪元素::before和::after的用法示例
2017/09/18 HTML / CSS
详解HTML5中的manifest缓存使用
2015/09/09 HTML / CSS
解释一下Windows的消息机制
2014/01/30 面试题
银行演讲稿范文
2014/01/03 职场文书
导游个人求职信范文
2014/03/23 职场文书
小学班主任寄语大全
2014/04/04 职场文书
委托公证书
2014/04/08 职场文书
元旦晚会开场白
2015/05/29 职场文书
党支部对转正的意见
2015/06/02 职场文书
高一地理教学工作总结
2015/08/12 职场文书
MySQL复制问题的三个参数分析
2021/04/07 MySQL