奇怪的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 之 没有mysql支持时的替代方案
Oct 09 PHP
discuz的php防止sql注入函数
Jan 17 PHP
jQuery EasyUI API 中文文档 - DateBox日期框
Oct 15 PHP
php引用传值实例详解学习
Nov 06 PHP
ubuntu12.04使用c编写php扩展模块教程分享
Dec 25 PHP
php微信公众开发之获取周边酒店信息的方法
Dec 22 PHP
PHP查询并删除数据库多列重复数据的方法(利用数组函数实现)
Feb 23 PHP
PHP浮点数的一个常见问题
Mar 10 PHP
PHP mysqli事务操作常用方法分析
Jul 22 PHP
ThinkPHP框架整合微信支付之Native 扫码支付模式一图文详解
Apr 09 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
Mar 24 PHP
php回调函数处理数组操作示例
Apr 13 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中使用Oracle数据库(6)
2006/10/09 PHP
php 图像函数大举例(非原创)
2009/06/20 PHP
ThinkPHP提示错误Fatal error: Allowed memory size的解决方法
2015/02/12 PHP
php实现用户注册密码的crypt加密
2017/06/08 PHP
PHP使用PDO创建MySQL数据库、表及插入多条数据操作示例
2019/05/30 PHP
解析arp病毒背后利用的Javascript技术附解密方法
2007/08/06 Javascript
用 Javascript 验证表单(form)中多选框(checkbox)值
2009/09/08 Javascript
JS继承 笔记
2011/07/13 Javascript
基于jquery实现的移入页面上空文本框时,让它变为焦点,移出清除焦点
2011/07/26 Javascript
读jQuery之十三 添加事件和删除事件的核心方法
2011/08/23 Javascript
JS动态获取当前时间,并写到特定的区域
2013/05/03 Javascript
用js代码改变单选框选中状态的简单实例
2013/12/18 Javascript
JavaScript判断微信浏览器实例代码
2016/06/13 Javascript
浅析JS获取url中的参数实例代码
2016/06/14 Javascript
JS实现禁止用户使用Ctrl+鼠标滚轮缩放网页的方法
2017/04/28 Javascript
微信小程序实现皮肤功能(夜间模式)
2017/06/18 Javascript
vue2.0 axios前后端数据处理实例代码
2017/06/30 Javascript
全选复选框JavaScript编写小结(附代码)
2017/08/16 Javascript
jQuery实现参数自定义的文字跑马灯效果
2018/08/15 jQuery
vue动态注册组件实例代码详解
2019/05/30 Javascript
[00:53]2015国际邀请赛 中国区预选赛一触即发
2015/05/14 DOTA
解读Django框架中的低层次缓存API
2015/07/24 Python
go和python变量赋值遇到的一个问题
2017/08/31 Python
pandas string转dataframe的方法
2018/04/11 Python
浅谈pytorch和Numpy的区别以及相互转换方法
2018/07/26 Python
Appium+python自动化怎么查看程序所占端口号和IP
2019/06/14 Python
python批量修改ssh密码的实现
2019/08/08 Python
tensorflow查看ckpt各节点名称实例
2020/01/21 Python
如何定义TensorFlow输入节点
2020/01/23 Python
使用python实现微信小程序自动签到功能
2020/04/27 Python
Python列表嵌套常见坑点及解决方案
2020/09/30 Python
HTML table 表格边框的实现思路
2019/10/12 HTML / CSS
浪漫婚礼主题活动策划方案
2014/09/15 职场文书
财务总监岗位职责范本
2015/04/03 职场文书
关于远足的感想
2015/08/10 职场文书
Java死锁的排查
2022/05/11 Java/Android