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实现将GB编码转换为UTF8
Nov 25 PHP
PHP 年龄计算函数(精确到天)
Jun 07 PHP
thinkphp的c方法使用示例
Feb 24 PHP
Yii框架中 find findAll 查找出制定的字段的方法对比
Sep 10 PHP
php中stdClass的用法分析
Feb 27 PHP
学习php设计模式 php实现适配器模式
Dec 07 PHP
php 的反射详解及示例代码
Aug 25 PHP
PHP数组生成XML格式数据的封装类实例
Nov 10 PHP
CMSPRESS 10行代码搞定 PHP无限级分类2
Mar 30 PHP
php curl批处理实现可控并发异步操作示例
May 09 PHP
PHP执行linux命令6个函数代码实例
Nov 24 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
2006/12/14 PHP
详谈配置phpstorm完美支持Codeigniter(CI)代码自动完成(代码提示)
2017/04/07 PHP
window.showModalDialog使用手册
2007/01/11 Javascript
JS支持带x身份证号码验证函数
2008/08/10 Javascript
学习ExtJS Panel常用方法
2009/10/07 Javascript
javascript 正则替换 replace(regExp, function)用法
2010/05/22 Javascript
Javascript操作cookie的函数代码
2012/10/03 Javascript
jQuery中ajax的get()方法用法实例
2014/12/26 Javascript
javascript实现tab响应式切换特效
2016/01/29 Javascript
AngularJS 单元测试(二)详解
2016/09/21 Javascript
input获取焦点时底部菜单被顶上来问题的解决办法
2017/01/24 Javascript
基于Node.js的WebSocket通信实现
2017/03/11 Javascript
Angular.JS中的指令引用template与指令当做属性详解
2017/03/30 Javascript
JS实现异步上传压缩图片
2017/04/22 Javascript
vue 列表页跳转详情页获取id以及详情页通过id获取数据
2019/03/27 Javascript
express如何解决ajax跨域访问session失效问题详解
2019/06/20 Javascript
mpvue微信小程序开发之实现一个弹幕评论
2019/11/24 Javascript
python装饰器使用方法实例
2013/11/21 Python
Python类的专用方法实例分析
2015/01/09 Python
Python3实现简单可学习的手写体识别(实例讲解)
2017/10/21 Python
对python指数、幂数拟合curve_fit详解
2018/12/29 Python
对Python中TKinter模块中的Label组件实例详解
2019/06/14 Python
django最快程序开发流程详解
2019/07/19 Python
CSS3中的opacity属性使用教程
2015/08/19 HTML / CSS
阿迪达斯墨西哥官方网站:adidas墨西哥
2017/11/03 全球购物
活动总结报告怎么写
2014/07/03 职场文书
商铺门前三包责任书
2014/07/25 职场文书
社区助残日活动总结
2014/08/29 职场文书
国际贸易实务实训报告
2014/11/05 职场文书
2014年学校禁毒工作总结
2014/12/23 职场文书
2015年机关党委工作总结
2015/05/23 职场文书
践行三严三实心得体会(2016推荐篇)
2016/01/06 职场文书
2016学习依法治国心得体会
2016/01/15 职场文书
2019商业计划书格式、范文
2019/04/24 职场文书
超外差式晶体管收音机的组装与统调
2021/04/22 无线电
mysql升级到5.7时,wordpress导数据报错1067的问题
2021/05/27 MySQL