奇怪的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 08 PHP
php $_SERVER当前完整url的写法
Nov 12 PHP
php 用checkbox一次性删除多条记录的方法
Feb 23 PHP
php park、unpark、ord 函数使用方法(二进制流接口应用实例)
Oct 19 PHP
免费的ip数据库淘宝IP地址库简介和PHP调用实例
Apr 08 PHP
php分页函数完整实例代码
Sep 22 PHP
PHP+jQuery 注册模块的改进(三):更新到Smarty3.1
Oct 14 PHP
PHP实现添加购物车功能
Mar 06 PHP
PHP使用preg_split和explode分割textarea存放内容的方法分析
Jul 03 PHP
PHP调用接口用post方法传送json数据的实例
May 31 PHP
laravel-admin解决表单select联动时,编辑默认没选上的问题
Sep 30 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
PHP7标量类型declare用法实例分析
2016/09/26 PHP
thinkPHP5.0框架配置格式、加载解析与读取方法
2017/03/17 PHP
PHP中quotemeta()函数的用法讲解
2019/04/04 PHP
windows 2008r2+php5.6.28环境搭建详细过程
2019/06/18 PHP
yii2.0框架数据库操作简单示例【添加,修改,删除,查询,打印等】
2020/04/13 PHP
js 中 document.createEvent的用法
2010/08/29 Javascript
JS 去前后空格大全(IE9亲测)
2013/07/15 Javascript
javascript对下拉列表框(select)的操作实例讲解
2013/11/29 Javascript
js实现通用的微信分享组件示例
2014/03/10 Javascript
setInterval计时器不准的问题解决方法
2014/05/08 Javascript
Bootstrap+jfinal实现省市级联下拉菜单
2016/05/30 Javascript
js轮播图代码分享
2016/07/14 Javascript
浅谈javascript中的Function和Arguments
2016/08/30 Javascript
使用JavaScript判断用户输入的是否为正整数(两种方法)
2017/02/05 Javascript
vue生命周期与钩子函数简单示例
2019/03/13 Javascript
使用JS判断页面是首次被加载还是刷新
2019/05/26 Javascript
[05:02]2014DOTA2 TI中国区预选赛精彩TOPPLAY第三弹
2014/06/25 DOTA
Python编写检测数据库SA用户的方法
2014/07/11 Python
Python中的descriptor描述器简明使用指南
2016/06/02 Python
使用Django和Python创建Json response的方法
2018/03/26 Python
详解python之协程gevent模块
2018/06/14 Python
Python3中详解fabfile的编写
2018/06/24 Python
python针对不定分隔符切割提取字符串的方法
2018/10/26 Python
使用PDF.JS插件在HTML中预览PDF文件的方法
2018/08/29 HTML / CSS
adidas瑞典官方网站:购买阿迪达斯鞋子和运动服
2019/12/11 全球购物
Java工程师面试集锦之Spring框架
2013/06/16 面试题
五一家具促销方案
2014/01/10 职场文书
工作决心书范文
2014/03/11 职场文书
施工安全责任书范本
2014/07/24 职场文书
2014党员四风对照检查材料思想汇报
2014/09/17 职场文书
银行贷款收入证明
2014/10/17 职场文书
个人先进事迹材料
2014/12/29 职场文书
大学生求职自荐信
2015/03/24 职场文书
装修公司工程部经理岗位职责
2015/04/09 职场文书
欧也妮葛朗台读书笔记
2015/06/30 职场文书
MySQL 用 limit 为什么会影响性能
2021/09/15 MySQL