PHP 引用是个坏习惯

代码如下:

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的内存管理机制有关系。
现在一切都明白了吧!今天,想了好几个小时,才把这个问题想通,不敢独享。
函数中的引用不是给你传参数方便的,而是让你实现,一个函数,可以有多个返回值的,所以,最好不要画蛇添足。
实际上,用引用它会降低性能。

(0)

相关推荐

  • php代码书写习惯优化小结

    (1)使用 static 静态方法比普通方法快4倍(2)echo输出快于print(3)连接字符使用 , 代替 .(4)循环之前先取出最大值,而不是在循环里面取值    正确的方法      $max = count($array);      for ($i=0;$i<$max;$i++) {      echo $i;      }    错误的方法      for ($i=0;$i<count($array);$i++) {      echo $i;      }(5)使用unset

  • 在PHP中养成7个面向对象的好习惯

    在 PHP 编程早期,PHP 代码在本质上是限于面向过程的.过程代码 的特征在于使用过程构建应用程序块.过程通过允许过程之间的调用提供某种程度的重用. 但是,没有面向对象的语言构造,程序员仍然可以把 OO 特性引入到 PHP 代码中.这样做有点困难并且会使代码难于阅读,因为它是混合范例(含有伪 OO 设计的过程语言).使用 PHP 代码中的 OO 构造 - 例如能够定义和使用类.能够构建使用继承的类之间的关系以及能够定义接口 - 可以更轻松地构建符合优秀 OO 实践的代码. 虽然没有过多模块化的

  • 编写安全 PHP应用程序的七个习惯深入分析

    在提及安全性问题时,需要注意,除了实际的平台和操作系统安全性问题之外,您还需要确保编写安全的应用程序.在编写 PHP 应用程序时,请应用下面的七个习惯以确保应用程序具有最好的安全性:•验证输入•保护文件系统•保护数据库•保护会话数据•保护跨站点脚本(Cross-site scripting,XSS)漏洞•检验表单 post•针对跨站点请求伪造(Cross-Site Request Forgeries,CSRF)进行保护验证输入在提及安全性问题时,验证数据是您可能采用的最重要的习惯.而在提及输入时

  • PHP 编程的 5个良好习惯

    根据具体的情况,一般的开发人员往往比优秀的开发人员的效率低 10%~20%.优秀的开发人员的效率更高,因为他们拥有丰富的经验和良好的编程习惯.不良的编程习惯将会影响到效率.本文通过展示一些良好的编程习惯,帮助您成为更优秀的程序员. 这些良好的编程习惯不仅能提高效率,还能让您编写出在应用程序的整个生命周期中易于维护的代码.编写出来的代码可能需要大量的维护:应用程序的维护是一笔很大的开支.养成良好的编程习惯能够提高设计质量(比如模块化),从而使代码更加容易理解,因此维护就更加容易,同时也降低维护成本

  • PHP大神的十大优良习惯

    php大神养成记,具体内容如下 1.多阅读手册和源代码 没什么比阅读手册更值得强调的事了–仅仅通过阅读手册你就可以学习到很多东西,特别是很多有关于字符串和数组的函数.就在这些函数里面包括许多有用的功能,如果你仔细阅读手册,你会经常发现在以往的项目开发过程中,很多时候你在"重复发明轮子",而实际上你只需要一个核心函数就可以完成相应的功能.手册是你的朋友.另外,现在有很多使用PHP开发的开源程序.为什么不去学习和借鉴呢?下载一份开源的PHP应用程序的源代码,仔细阅读它吧.也许越大的项目越值

  • 国外PHP程序员的13个好习惯小结

    也就是本文列举的这13个PHP编码好习惯,如果你有更好的建议,欢迎在本文后面的评论中发表,我这个人是喜欢求知的1.使用select从相同的数据库查询信息时,使用一个join语句一次性整齐地获取你需要的所有信息,而不要写多个mysql_query/while/mysql_fetch_array语句. 2.如果你在多个文件中调用了一个数据库连接,创建一个connection.php文件保存你的连接变量,在需要的地方将这个文件包括进来. 3.对于小型项目,将你所有的函数写在一个文件中,如果是大型项目就

  • [php]正则表达式的五个成功习惯

    正则表达式难于书写.难于阅读.难于维护,经常错误匹配意料不到的文本或者错过了有效的文本,这些问题都是由正则表达式的表现和能力引起的.每个元字符(metacharacter)的能力和细微差别组合在一起,使得代码不借助于智力技巧就无法解释.       许多包含一定特性的工具使阅读和编写正则表达式变得容易了,但是它们又很不符合习惯.对于很多程序员来说,书写正则表达式就是一种魔法艺术.他们坚持自己所知道的特征并持有绝对乐观的态度.如果你愿意采用本文所探讨的五个习惯,你将可以让你设计的正则表达式经受的住

  • 10条PHP编程习惯助你找工作

    过去的几周对我来说是一段相当复杂的经历.我们公司进行了大裁员,我是其中之一,但却体验到了其中的乐趣.我从来没有被开除过,所以很难不去想得太多.我开始浏览招聘板块,一个全职PHP程序员的职位很吸引人,所以我寄去了简历并获得了面试机会.在面试之间,我和其主要的程序员们在咨询电话中聊了聊,最后他们给我出了一套测试题,其中有一道很耐人寻味. 找出以下代码的错误之处: <?function baz($y $z) { $x = new Array(); $x[sales] = 60; $x[profit]

  • VB编程的八个优良习惯第1/2页

    VB编程的八个优良习惯 1."&"替换"+"  2.变量命名大小写,语句错落有秩,源代码维护方面  3.请养成以下的"对象命名约定"良好习惯 4.在简单的选择条件情况下,使用IIf()函数  5.尽量使用Debug.Print进行调试  6.在重复对某一对象的属性进行修改时,尽量使用With....End With  7.MsgBox中尽量使用消息图标,这样程序比较有规范  8.在可能的情况下使用枚举 1."&"

  • PHP 引用是个坏习惯

    复制代码 代码如下: 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

  • 程序员写Python时的5个坏习惯,你有几条?

    很多文章都有介绍怎么写好 Python,我今天呢,相反,说说写代码时的几个坏习惯.有的习惯会让 Bug 变得隐蔽难以追踪,当然,也有的并没有错误,只是个人觉得不够优雅. 注意:示例代码在 Python 3.6 环境下编写 1 用列表作函数的默认参数 看下面这个例子 def func(a, b=[]): b.append(a) print(f'a: {a}') print(f'b: {b}') func(1) func(2) 正常我们期望的结果应该是这样的 a: 1 b: [1] a: 2 b:

  • 成为好程序员必须避免的5个坏习惯

    当你开始成为一个程序员的时候,在编程的时候很容易陷入下面所述的一些坏习惯. 作为一名程序员,犯错误不可避免,这是你学习编程课程中的一部分.在你的职业生涯中你会犯很多错误 – 有的特别.有的普遍 – 通过这些错误你可以学习如何避免在将来再犯同样的错误. 但是如果你是一个初学者,你犯的错误可能会比其他人更频繁.那么如何才能避免大部分程序员每天都犯的这些普遍的错误呢? 想要避免错误,就要对它有所了解.这也是为什么我要和大家分享一些在我们的程序员生涯中阻碍我们成长的普遍错误. 在开始之前,你可能想知道为

  • Docker容器的应用中,10个要不得的坏习惯

    毋庸置疑,容器已经成为企业IT基础设施中必不可少的部分,它具有许多的优点,比如: 第一:容器是不可变的--操作系统,库版本,配置,文件夹和应用程序都包装在容器内.你保证在质量检查中测试过的同一镜像将以相同的行为到达生产环境. 第二:容器很轻--容器的内存占用量很小.容器将只为主要进程分配内存,而不是数百或数千MB. 第三:容器非常快--可以像启动典型Linux进程一样快地启动容器.你可以在几秒钟内启动一个新容器,而不是几分钟. 但是,许多用户仍然像对待典型虚拟机一样对待容器,而忘记了容器具有重要

  • 梳理总结Python开发中需要摒弃的18个坏习惯

    废话不多说,我们开始学习吧! 1.拼接字符串用 + 号 坏的做法: def manual_str_formatting(name, subscribers): if subscribers > 100000: print("Wow " + name + "! you have " + str(subscribers) + " subscribers!") else: print("Lol " + name + "

  • Docker容器应用中,10个要不得的坏习惯

    毋庸置疑,容器已经成为企业IT基础设施中必不可少的部分,它具有许多的优点,比如: 第一:容器是不可变的--操作系统,库版本,配置,文件夹和应用程序都包装在容器内.你保证在质量检查中测试过的同一镜像将以相同的行为到达生产环境. 第二:容器很轻--容器的内存占用量很小.容器将只为主要进程分配内存,而不是数百或数千MB. 第三:容器非常快--可以像启动典型Linux进程一样快地启动容器.你可以在几秒钟内启动一个新容器,而不是几分钟. 但是,许多用户仍然像对待典型虚拟机一样对待容器,而忘记了容器具有重要

  • 5个书写JavaScript代码的坏习惯,看看你中枪了没?

    Javascript在互联网上名声很臭,但你又很难再找到一个像它这样如此动态.如此被广泛使用.如此根植于我们的生活中的另外一种语言.它的低学习门槛让很多人都称它为学前脚本语言,它另外一个让人嘲笑的东西是动态语言的概念是偏偏使用了高标准的静态数据类型.其实,你和Javascript都站错了立场,而现在,你让Javascript很生气.这里有五个原因能说明你的Javascript技术很烂. 1. 你没有使用命名空间. 是否还记得在大学里老师告诉你不要在家庭作业里使用全局变量?Javascript里的

  • 看UNIX高手的10个习惯第1/2页

    引言 当您经常使用某个系统时,往往会陷入某种固定的使用模式.有时,您没有养成以尽可能最好的方式做事的习惯.有时,您的不良习惯甚至会导致出现混乱.纠正此类缺点的最佳方法之一,就是有意识地采用抵制这些坏习惯的好习惯.本文提出了 10 个值得采用的 UNIX 命令行习惯--帮助您克服许多常见使用怪癖,并在该过程中提高命令行工作效率的好习惯.下面列出了这 10 个好习惯,之后对进行了更详细的描述. 采用 10 个好习惯 要采用的十个好习惯为: 在单个命令中创建目录树.  更改路径:不要移动存档.  将命

  • javascript中的对象和数组的应用技巧

    javascript已经用了有三年多了,但是对一些细节的东西还是一知半解,比如对象和数组,一直都在用一些最基本的操作.这是我学习的一个坏习惯--懒,很多东西都是了解而已,看到稍微复杂点的逻辑就没有耐心.就像学asp时不知道asp跟脚本的关系,学html不知道什么是DOM...结果是学得慢且不扎实,碰到稍复杂的问题就歇菜(扯远了). 今天继续优化脚本,尝试着合并一些数组时碰到问题,才算去仔细看了手册,做了一些测试. javascript1.2之后允许使用[]创建数组: var firstArray

随机推荐