奇怪的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 相关文章推荐
人尽可用的Windows技巧小贴士之下篇
Mar 22 PHP
用PHP程序实现支持页面后退的两种方法
Jun 30 PHP
php 常用类汇总 推荐收藏
May 13 PHP
《PHP编程最快明白》第二讲 数字、浮点、布尔型、字符串和数组
Nov 01 PHP
PHP调用Linux的命令行执行文件压缩命令
Jan 27 PHP
php中导出数据到excel时数字变为科学计数的解决方法
Feb 03 PHP
全面解读PHP的人气开发框架Laravel
Oct 15 PHP
php 根据自增id创建唯一编号类
Apr 06 PHP
ThinkPHP下表单令牌错误与解决方法分析
May 20 PHP
PHP中的日期时间处理利器实例(Carbon)
Jun 09 PHP
如何在PHP环境中使用ProtoBuf数据格式
Jun 19 PHP
phpStorm2020 注册码
Sep 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
php与php MySQL 之间的关系
2009/07/17 PHP
PHPMailer邮件发送的实现代码
2013/05/04 PHP
php增删改查示例自己写的demo
2013/09/04 PHP
PHP图像处理类库及演示分享
2015/05/17 PHP
浅谈laravel5.5 belongsToMany自身的正确用法
2019/10/17 PHP
JavaScript 创建运动框架的实现代码
2013/05/08 Javascript
jQuery照片伸缩效果不影响其他元素的布局
2014/05/09 Javascript
js重写alert控件(适合学习js的新手朋友)
2014/08/24 Javascript
z-blog SyntaxHighlighter 长代码无法换行解决办法(jquery)
2014/11/16 Javascript
js实现二代身份证号码验证详解
2014/11/20 Javascript
javascript获取四位数字或者字母的随机数
2015/01/09 Javascript
简介JavaScript中用于处理正切的Math.tan()方法
2015/06/15 Javascript
JavaScript 数组some()和filter()的用法及区别
2016/05/20 Javascript
jQuery+ThinkPHP+Ajax实现即时消息提醒功能实例代码
2017/03/21 jQuery
整理关于Bootstrap导航的慕课笔记
2017/03/29 Javascript
JavaScript使用原型和原型链实现对象继承的方法详解
2017/04/05 Javascript
详解JavaScript中操作符和表达式
2018/09/12 Javascript
关于element的表单组件整理笔记
2021/02/05 Javascript
[00:29]2019完美世界全国高校联赛(秋季赛)总决赛海口落幕
2019/12/10 DOTA
python删除文件示例分享
2014/01/28 Python
python3音乐播放器简单实现代码
2020/04/20 Python
Python的装饰器使用详解
2017/06/26 Python
解决安装pyqt5之后无法打开spyder的问题
2019/12/13 Python
TensorFlow基本的常量、变量和运算操作详解
2020/02/03 Python
python对XML文件的操作实现代码
2020/03/27 Python
CSS3之2D与3D变换的实现方法
2019/01/28 HTML / CSS
使用HTML5 Geolocation实现一个距离追踪器
2018/04/09 HTML / CSS
Michael Kors澳大利亚官网:世界知名的奢侈饰品和成衣设计师
2020/02/13 全球购物
个人生活学习自我评价范文
2013/11/26 职场文书
法人授权委托书公证范本
2014/09/14 职场文书
2016年9月份红领巾广播稿
2015/12/21 职场文书
2016年师德先进个人事迹材料
2016/02/29 职场文书
opencv-python图像配准(匹配和叠加)的实现
2021/06/23 Python
python基础之类方法和静态方法
2021/10/24 Python
Win11 Build 22000.51版本文件资源管理器“命令栏”和上下文菜单有什么新变化?
2021/11/21 数码科技
Python绘画好看的星空图
2022/03/17 Python