PHP 引用是个坏习惯


Posted in PHP onMarch 12, 2010
function binsearch(&$arr, $key, $value) 
{ 
$low = 0; 
$high = count($arr); 
while ($low <= $high) { 
$mid = floor($low + ($high - $low) / 2); 
$item = $arr[$mid][$key]; 
if ($item == $value) { 
return $mid; 
} else if ($value > $item) { 
$low = $mid + 1; 
} else { 
$high = $mid - 1; 
} 
} 
return false; 
}

在这里,$mid 采用了先减后加的方法计算,目的是为了防止整数的溢出。不是故意写复杂了。
我用下面的代码进行测试:
$data = array(); 
for ($i = 0; $i < 1000000; $i++) 
{ 
$data[] = array("sq" => $i * 2); 
} 
var_dump(binsearch($data, "sq", 10000));

发现,binsearch 的时候,总是要花个 0.2s左右。理论上来说,100万的数据,最多也就是循环20次。怎么会这样慢呢。
后来监控了一下内存,data 数组 占用了 230M 的内存。而 binsearch 的时候,占用了60K 的内存。但是,理论上来说,binsearch
不应该占用如此多的内存。因为,我觉得,我已经用引用了,根本就没有对data 的结构进行修改。
我也是百思不得其解,后来,我把引用参数去掉,居然 binsearch 只要 0.0002s ,看来是引用耗费了大量的cpu 资源。
PHP 内部遵循一个copy on write 的原则。实际上这个引用是多余的。
但是为什么,加了引用速度会变慢呢?今天重点就谈谈这个问题。明白道理后,大家一定知道怎么用引用了。
如果在binsearch 调用前,直接 $a = &$data,这个引用的速度会非常的快。看来肯定不是引用本身产生的问题。
这个问题,实际上涉及了zend 引擎如何管理PHP变量。
先看下面的问题:
<?php 
function demo(&$a, &$b) { $a =& $b; } 
$a = 1; 
$b = 2; 
demo($a, $b); 
$b = 3; 
print $a; 
?>

$a 输出是多少呢?不错,是2. 不过,我一开始觉得是3。
那么怎么解释上面这个问题呢?
实际上,函数的参数引用是这样进行的。
$tmp = $a; 
$a1 = &$tmp; 
$a = $tmp; 
unset($a1, $tmp);

这里,引用的实际上是一个临时变量。这个时候,$tmp 是带引用属性的,而$a 变量不是带引用属性的。
根据zend引擎管理内存的方法,在内部,不能用一个zval 来表示,必须强制分离这个zval。
用这样的理解方法,上面的问题就解决了。函数内部,不会改变函数外部的引用特性。这也是PHP
不赞成用 calltime_by_ref 的原因,而选择上面如此低效的拷贝方法。
下面的分析,也能证明,在传递参数时,的确发生了拷贝。
在 binsearch 函数里面。
$data[0] = 1;
这样,就会发生一次$data 所在zval 的拷贝。内存使用量 就是 60K。和函数调用加引用一模一样。
可能很多人会疑问,为什么不是多了230M呢,这其实就是PHP的高明之处,数组Key 对应的是一个zval的指针。(内部是一个哈希表)
所以,只要把这些指针复制一遍就就好了,数据不用复制。但是,100万的PHP 哈希表实际上要占用 50M 内存。为什么只有60K呢。
在 binsearch 函数的外面,运行
$t = $data; 
$t[0] = 1; 
unset($t);

果然,多了60K 的内存。估计和PHP的内存管理机制有关系。
现在一切都明白了吧!今天,想了好几个小时,才把这个问题想通,不敢独享。
函数中的引用不是给你传参数方便的,而是让你实现,一个函数,可以有多个返回值的,所以,最好不要画蛇添足。
实际上,用引用它会降低性能。
PHP 相关文章推荐
用PHP实现登陆验证码(类似条行码状)
Oct 09 PHP
php+mysql写的简单留言本实例代码
Jul 25 PHP
在PHP中检查PHP文件是否有语法错误的方法
Dec 23 PHP
Windows下XDebug 手工配置与使用说明
Jul 11 PHP
超小PHP小马小结(方便查找后门的朋友)
May 05 PHP
PHP版网站缓存加快打开速度的方法分享
Jun 03 PHP
php对图像的各种处理函数代码小结
Jul 08 PHP
ThinkPHP文件上传实例教程
Aug 22 PHP
Yii视图操作之自定义分页实现方法
Jul 14 PHP
[原创]PHP实现字节数Byte转换为KB、MB、GB、TB的方法
Aug 31 PHP
ThinkPHP5+Layui实现图片上传加预览功能
Aug 17 PHP
浅析PHP反序列化中过滤函数使用不当导致的对象注入问题
Feb 15 PHP
PHP 页面编码声明方法详解(header或meta)
Mar 12 #PHP
用PHP获取Google AJAX Search API 数据的代码
Mar 12 #PHP
PHP开启gzip页面压缩实例代码
Mar 11 #PHP
php checkdate、getdate等日期时间函数操作详解
Mar 11 #PHP
PHP 5.3新特性命名空间规则解析及高级功能
Mar 11 #PHP
PHP Memcached + APC + 文件缓存封装实现代码
Mar 11 #PHP
了解Joomla 这款来自国外的php网站管理系统
Mar 11 #PHP
You might like
php下获取http状态的实现代码
2014/05/09 PHP
Laravel中扩展Memcached缓存驱动实现使用阿里云OCS缓存
2015/02/10 PHP
php从给定url获取文件扩展名的方法
2015/03/14 PHP
twig模板常用语句实例小结
2016/02/04 PHP
php遍历替换目录下文件指定内容的方法
2016/11/10 PHP
在textarea中显示html页面的javascript代码
2007/04/20 Javascript
js 页面执行时间计算代码
2009/03/04 Javascript
Jquery判断$(&quot;#id&quot;)获取的对象是否存在的方法
2013/09/25 Javascript
javascript日期计算实例分析
2015/06/29 Javascript
js实现可折叠展开的手风琴菜单效果
2015/09/07 Javascript
jQuery ajaxForm()的应用
2016/10/14 Javascript
基于JavaScript中字符串的match与replace方法(详解)
2017/12/04 Javascript
js如何找出字符串中的最长回文串
2018/06/04 Javascript
vue.js 中使用(...)运算符报错的解决方法
2018/08/09 Javascript
JS中准确判断变量类型的方法
2020/06/01 Javascript
vue 点击其他区域关闭自定义div操作
2020/07/17 Javascript
在Vue中使用Echarts实例图的方法实例
2020/10/10 Javascript
Python解析最简单的验证码
2016/01/07 Python
利用Python中的pandas库对cdn日志进行分析详解
2017/03/07 Python
Python装饰器模式定义与用法分析
2018/08/06 Python
Python参数解析模块sys、getopt、argparse使用与对比分析
2019/04/02 Python
python利用Opencv实现人脸识别功能
2019/04/25 Python
python 获取等间隔的数组实例
2019/07/04 Python
python安装virtualenv虚拟环境步骤图文详解
2019/09/18 Python
使用Python爬虫库requests发送表单数据和JSON数据
2020/01/25 Python
python实现密码验证合格程序的思路详解
2020/06/01 Python
Python大批量搜索引擎图像爬虫工具详解
2020/11/16 Python
python 爬取英雄联盟皮肤并下载的示例
2020/12/04 Python
卡西欧G-SHOCK英国官网: 防水防震手表
2018/01/08 全球购物
奥斯汀独木舟和皮划艇:Austin Canoe & Kayak
2018/05/22 全球购物
Chupi官网:在爱尔兰手工制作的订婚、结婚戒指和精美珠宝
2020/09/28 全球购物
自我评价范文点评
2013/12/04 职场文书
学术会议欢迎词
2014/01/09 职场文书
少先队活动总结
2014/08/29 职场文书
2015年度优秀员工推荐信
2015/03/23 职场文书
2016应届毕业生就业指导课心得体会
2016/01/15 职场文书