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 相关文章推荐
同台服务器使用缓存APC效率高于Memcached的演示代码
Feb 16 PHP
PHP读取CURL模拟登录时生成Cookie文件的方法
Nov 04 PHP
php+html5使用FormData对象提交表单及上传图片的方法
Feb 11 PHP
ubuntu下配置nginx+php+mysql详解
Sep 10 PHP
php 利用array_slice函数获取随机数组或前几条数据
Sep 30 PHP
WordPress主题制作之模板文件的引入方法
Dec 28 PHP
PHP读取、解析eml文件及生成网页的方法示例
Sep 04 PHP
PHP实现的超长文本分页显示功能示例
Jun 04 PHP
详解Laravel5.6 Passport实现Api接口认证
Jul 27 PHP
PHP获取对象属性的三种方法实例分析
Jan 03 PHP
Laravel框架模型的创建及模型对数据操作示例
May 07 PHP
详解thinkphp的Auth类认证
May 28 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
做个自己站内搜索引擎
2006/10/09 PHP
php 动态执行带有参数的类方法
2009/04/10 PHP
提交表单后 PHP获取提交内容的实现方法
2016/05/25 PHP
thinkphp框架page类与bootstrap分页(美化)
2017/06/25 PHP
PHP实现的二分查找算法实例分析
2017/12/19 PHP
js获取提交的字符串的字节数
2009/02/09 Javascript
jQuery 对象中的类数组操作
2009/04/27 Javascript
基于JQuery的列表拖动排序实现代码
2013/10/01 Javascript
控制台报错object is not a function的解决方法
2014/08/24 Javascript
js实现键盘自动打字效果
2016/12/23 Javascript
判断横屏竖屏(三种)
2017/02/13 Javascript
COM组件中调用JavaScript函数详解及实例
2017/02/23 Javascript
JSON是什么?有哪些优点?JSON和XML的区别?
2019/04/29 Javascript
JS实现手写 forEach算法示例
2020/04/29 Javascript
uniapp微信小程序实现一个页面多个倒计时
2020/11/01 Javascript
[01:06]DOTA2小知识课堂 Ep.01 TP出门不要忘记帮队友灌瓶哦
2019/12/05 DOTA
python Django批量导入数据
2016/03/25 Python
Python代码打开本地.mp4格式文件的方法
2019/01/03 Python
Python HTMLTestRunner可视化报告实现过程解析
2020/04/10 Python
Python爬取网页信息的示例
2020/09/24 Python
python 指定源路径来解决import问题的操作
2021/03/04 Python
非常震撼的纯CSS3人物行走动画
2016/02/24 HTML / CSS
利用CSS3的border-radius绘制太极及爱心图案示例
2016/05/17 HTML / CSS
FOREO斐珞尔官方旗舰店:LUNA露娜洁面仪
2018/03/11 全球购物
英国日常交易网站:Wowcher
2018/09/04 全球购物
StubHub澳大利亚:购买或出售您的门票
2019/08/01 全球购物
求职自荐信范文格式
2013/11/29 职场文书
业务员薪酬管理制度
2014/01/15 职场文书
开业庆典策划方案
2014/02/18 职场文书
2014年新生军训方案
2014/05/01 职场文书
宪法宣传周工作方案
2014/05/26 职场文书
2014年控辍保学工作总结
2014/12/08 职场文书
幼儿园教师岗位职责
2015/04/02 职场文书
2015年食品安全宣传周活动总结
2015/07/09 职场文书
养成教育主题班会
2015/08/13 职场文书
Elasticsearch Recovery 详细介绍
2022/04/19 Java/Android