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学习之整理字符串
Apr 17 PHP
php获取随机数组列表的方法
Nov 13 PHP
php中debug_backtrace、debug_print_backtrace和匿名函数用法实例
Dec 01 PHP
简单实用的PHP防注入类实例
Dec 05 PHP
ThinkPHP入口文件设置及相关注意事项分析
Dec 05 PHP
ThinkPHP中使用ajax接收json数据的方法
Dec 18 PHP
PHP CURL 多线程操作代码实例
May 13 PHP
Laravel Memcached缓存驱动的配置与应用方法分析
Oct 08 PHP
YII2自动登录Cookie总是失效的解决方法
Jun 28 PHP
PHP编程快速实现数组去重的方法详解
Jul 22 PHP
Thinkphp开发--集成极光推送
Sep 15 PHP
PHP实现Snowflake生成分布式唯一ID的方法示例
Aug 30 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基础知识:类与对象(4) 范围解析操作符(::)
2006/12/13 PHP
php动态添加url查询参数的方法
2015/04/14 PHP
php图像处理类实例
2015/07/28 PHP
Yii2框架自定义类统一处理url操作示例
2019/05/25 PHP
PHP程序守护进程化实现方法详解
2020/07/16 PHP
Jquery 数组操作大全个人总结
2013/11/13 Javascript
js读取配置文件自写
2014/02/11 Javascript
JavaScript实现的图像模糊算法代码分享
2014/04/22 Javascript
用js通过url传参把数据从一个页面传到另一个页面
2014/09/01 Javascript
js事件源window.event.srcElement兼容性写法(详解)
2016/11/25 Javascript
jquery实现表单获取短信验证码代码
2017/03/13 Javascript
Vue中引入样式文件的方法
2017/08/18 Javascript
浅析java线程中断的办法
2018/07/29 Javascript
vue router导航守卫(router.beforeEach())的使用详解
2019/04/19 Javascript
vue中使用[provide/inject]实现页面reload的方法
2019/09/30 Javascript
JavaScript中的this原理及6种常见使用场景详解
2020/02/14 Javascript
原生JavaScript创建不可变对象的方法简单示例
2020/05/07 Javascript
利用Vue的v-for和v-bind实现列表颜色切换
2020/07/17 Javascript
Ant Design Pro 之 ProTable使用操作
2020/10/31 Javascript
Python查看多台服务器进程的脚本分享
2014/06/11 Python
Python解惑之整数比较详解
2017/04/24 Python
简单谈谈Python的pycurl模块
2018/04/07 Python
python批量替换多文件字符串问题详解
2018/04/22 Python
python模拟菜刀反弹shell绕过限制【推荐】
2019/06/25 Python
基于python3.7利用Motor来异步读写Mongodb提高效率(推荐)
2020/04/29 Python
python Pexpect模块的使用
2020/12/25 Python
matplotlib之pyplot模块坐标轴标签设置使用(xlabel()、ylabel())
2021/02/22 Python
乌克兰排名第一的在线旅游超市:Farvater.Travel
2020/01/02 全球购物
英国在线购买轮胎、预订汽车、汽车维修和装配网站:Protyre
2020/04/12 全球购物
设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
2014/12/30 面试题
酒店开业庆典策划方案
2014/05/28 职场文书
人大调研汇报材料
2014/08/14 职场文书
全国法院系统开展党的群众路线教育实践活动综述(全文)
2014/10/25 职场文书
北大自主招生自荐信
2015/03/04 职场文书
房地产公司工程部经理岗位职责
2015/04/09 职场文书
移除Selenium中window.navigator.webdriver值
2022/06/10 Python