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 相关文章推荐
IIS环境下快速安装、配置和调试PHP5.2.0
Dec 17 PHP
php一句话cmdshell新型 (非一句话木马)
Apr 18 PHP
php数组函数序列之next() - 移动数组内部指针到下一个元素的位置,并返回该元素值
Oct 31 PHP
PHP Class&amp;Object -- PHP 自排序二叉树的深入解析
Jun 25 PHP
php生成图形验证码几种方法小结
Aug 15 PHP
php switch语句多个值匹配同一代码块的实现
Mar 03 PHP
php面向对象中的魔术方法中文说明
Mar 04 PHP
Laravel 5 框架入门(三)
Apr 09 PHP
php微信公众平台开发之获取用户基本信息
Aug 17 PHP
十个PHP高级应用技巧果断收藏
Sep 25 PHP
PHP文件缓存类实现代码
Oct 26 PHP
PHP使用imagick扩展实现合并图像的方法
Apr 25 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
json的键名为数字时的调用方式(示例代码)
2013/11/15 PHP
smarty内置函数config_load用法实例
2015/01/22 PHP
Codeigniter中集成smarty和adodb的方法
2016/03/04 PHP
PHP基于phpqrcode类生成二维码的方法详解
2018/03/14 PHP
jquery 插件学习(四)
2012/08/06 Javascript
原生javascript兼容性测试实例
2013/07/01 Javascript
js判断undefined类型,undefined,null, 的区别详细解析
2013/12/16 Javascript
javascript实现密码验证
2015/11/10 Javascript
JS实现随机颜色的3种方法与颜色格式的转化
2017/01/05 Javascript
JS中解决谷歌浏览器记住密码输入框颜色改变功能
2017/02/13 Javascript
Bootstrap DateTime Picker日历控件简单应用
2017/03/25 Javascript
基于javaScript的this指向总结
2017/07/22 Javascript
JavaScript实现移动端页面按手机屏幕分辨率自动缩放的最强代码
2017/08/18 Javascript
Vue2 配置 Axios api 接口调用文件的方法
2017/11/13 Javascript
bootstrap fileinput插件实现预览上传照片功能
2018/01/23 Javascript
浅谈让你的代码更简短,更整洁,更易读的ES6小技巧
2018/10/25 Javascript
vant(ZanUi)结合async-validator实现表单验证的方法
2018/12/06 Javascript
vue中keep-alive组件的入门使用教程
2019/06/06 Javascript
通过扫小程序码实现网站登陆功能
2019/08/22 Javascript
文章或博客自动生成章节目录索引(支持三级)的实现代码
2020/05/10 Javascript
基于JS实现快速读取TXT文件
2020/08/25 Javascript
[01:10:30]DOTA2-DPC中国联赛正赛 Dragon vs Dynasty BO3 第一场 3月4日
2021/03/11 DOTA
python绘制直线的方法
2018/06/30 Python
Python使用googletrans报错的解决方法
2018/09/25 Python
Python多继承以及MRO顺序的使用
2019/11/11 Python
Python绘图实现显示中文
2019/12/04 Python
详解通过变换矩阵实现canvas的缩放功能
2019/01/14 HTML / CSS
俄罗斯一家时尚女装商店:Charuel
2019/12/04 全球购物
课改先进个人汇报材料
2014/01/26 职场文书
2014年小班元旦活动方案
2014/02/16 职场文书
田径运动会开幕式及主持词
2014/03/28 职场文书
党的群众路线教育实践活动专题组织生活会发言材料
2014/10/17 职场文书
委托公证书格式
2015/01/26 职场文书
离婚起诉书怎么写
2015/05/19 职场文书
vue使用refs获取嵌套组件中的值过程
2022/03/31 Vue.js
项目中Nginx多级代理是如何获取客户端的真实IP地址
2022/05/30 Servers