奇怪的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实现的zip文件内容比较类
Sep 24 PHP
IIS下PHP的三种配置方式对比
Nov 20 PHP
thinkphp四种url访问方式详解
Nov 28 PHP
PHP 魔术变量和魔术函数详解
Feb 25 PHP
php压缩和解压缩字符串的方法
Mar 14 PHP
php在apache环境下实现gzip配置方法
Apr 02 PHP
ThinkPHP中数据操作案例分析
Sep 27 PHP
讲解WordPress开发中一些常用的debug技巧
Dec 18 PHP
windows下的WAMP环境搭建图文教程(推荐)
Jul 27 PHP
PHP基于rabbitmq操作类的生产者和消费者功能示例
Jun 16 PHP
php异常处理捕获错误整理
Sep 23 PHP
使用laravel的Eloquent模型如何获取数据库的指定列
Oct 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
Zend Framework教程之Zend_Layout布局助手详解
2016/03/04 PHP
PHP自定义函数实现数组比较功能示例
2017/10/19 PHP
PHP单例模式实例分析【防继承,防克隆操作】
2019/05/22 PHP
使用 PHP Masked Package 屏蔽敏感数据的实现方法
2019/10/15 PHP
JavaScript.The.Good.Parts阅读笔记(二)作用域&amp;闭包&amp;减缓全局空间污染
2010/11/16 Javascript
javascript 函数参数限制说明
2010/11/19 Javascript
Array的push与unshift方法性能比较分析
2011/03/05 Javascript
JQuery设置时间段下拉选择实例
2014/12/30 Javascript
原生js实现移动端瀑布流式代码示例
2015/12/18 Javascript
jquery实现文本框的禁用和启用
2016/12/07 Javascript
用move.js库实现百叶窗特效
2017/02/08 Javascript
详解Web使用webpack构建前端项目
2017/09/23 Javascript
Vue 表情包输入组件的实现代码
2019/01/21 Javascript
vue实现form表单与table表格的数据关联功能示例
2019/01/29 Javascript
JavaScript中的ES6 Proxy的具体使用
2019/06/16 Javascript
Angular进行简单单元测试的实现方法实例
2020/08/16 Javascript
Python中的Matplotlib模块入门教程
2015/04/15 Python
Django ManyToManyField 跨越中间表查询的方法
2018/12/18 Python
在Python中append以及extend返回None的例子
2019/07/20 Python
解决Djang2.0.1中的reverse导入失败的问题
2019/08/16 Python
Python 仅获取响应头, 不获取实体的实例
2019/08/21 Python
解决pycharm编辑区显示yaml文件层级结构遇中文乱码问题
2020/04/27 Python
Python使用matplotlib绘制圆形代码实例
2020/05/27 Python
Python读取多列数据以及用matplotlib制作图表方法实例
2020/09/23 Python
python 可视化库PyG2Plot的使用
2021/01/21 Python
HTML5如何为形状图上颜色怎么绘制具有颜色和透明度的矩形
2014/06/23 HTML / CSS
意大利婴儿产品网上商店:Mukako
2018/10/14 全球购物
创意爱尔兰礼物:Creative Irish Gifts
2020/01/29 全球购物
知识竞赛活动方案
2014/02/18 职场文书
机电一体化求职信
2014/03/10 职场文书
经典广告词大全
2014/03/14 职场文书
小学班干部竞选演讲稿
2014/04/24 职场文书
助人为乐模范事迹材料
2014/06/02 职场文书
大学生翘课检讨书范文
2014/10/06 职场文书
2014年变电站工作总结
2014/12/19 职场文书
董事长年会致辞
2015/07/29 职场文书