百度工程师讲PHP函数的实现原理及性能分析(三)


Posted in PHP onMay 13, 2015

常用php函数实现及介绍

count
count是我们经常用到的一个函数,其功能是返回一个数组的长度。
count这个函数,其复杂度是多少呢? 一种常见的说法是count函数会遍历整个数组然后求出元素个数,因此复杂度是O(n)。那实际情况是不是这样呢?我们回到count的实现来看一下,通过源码可以发现,对于数组的count操作,函数最终的路径是zif_count-> php_count_recursive-> zend_hash_num_elements,而zend_hash_num_elements的行为是 return ht->nNumOfElements,可见,这是一个O(1)而不是O(n)的操作。实际上,数组在php底层就是一个hash_table,对于hash表,zend中专门有一个元素nNumOfElements记录了当前元素的个数,因此对于一般的count实际上直接就返回了这个值。由此,我们得出结论: count是O(1)的复杂度,和具体数组的大小无关。
非数组类型的变量,count的行为时怎样?对于未设置变量返回0,而像int、double、string等则会返回1

strlen
Strlen用于返回一个字符串的长度。那么,他的实现原理是如何的呢?我们都知道在c中strlen是一个o(n)的函数,会顺序遍历字符串直到遇到\0,然后出长度。Php中是否也这样呢?答案是否定的,php里字符串是用一个复合结构来描述,包括指向具体数据的指针和字符串长度(和c++中string类似),因此 strlen就直接返回字符串长度了,是常数级别的操作。另外,对于非字符串类型的变量调用strlen,它会首先将变量强制转换为字符串再求长度,这点需要注意。

isset和array_key_exists
这两个函数最常见的用法都是判断一个 key是否在数组中存在。但是前者还可以用于判断一个变量是否被设置过。如前文所述,isset并非真正的函数,因此它的效率会比后者高很多。推荐用它代替array_key_exists。
array_push和array[]
两者都是往数组尾部追加一个元素。不同的是前者可以一次push多个。他们最大的区别在于一个是函数一个是语言结构,因此后者效率要更高。因此如果只是普通的追加元素,建议使用array []。

rand和mt_rand
两者都是提供产生随机数的功能,前者使用 libc标准的rand。后者用了 Mersenne Twister 中已知的特性作为随机数发生器,它可以产生随机数值的平均速度比 libc 提供的 rand() 快四倍。因此如果对性能要求较高,可以考虑用mt_rand代替前者。我们都知道,rand产生的是伪随机数,在C中需要用srand显示指定种子。但是在php中,rand会自己帮你默认调用一次srand,一般情况下不需要自己再显示的调用。需要注意的是,如果特殊情况下需要调用srand时,一定要配套调用。就是说srand对于rand,mt_srand对应srand,切不可混合使用,否则是无效的。

sort和 usort
两者都是用于排序,不同的是前者可以指定排序策略,类似我们C里面的qsort和C++的sort。在排序上两者都是采用标准的快排来实现,对于有排序需求的,如非特殊情况调用php提供的这些方法就可以了,不用自己重新实现一遍,效率会低很多。原因见前文对于用户函数和内置函数的分析比对。

urlencode和rawurlencode
这两个都是用于 url编码, 字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数。两者唯一的区别在于对于空格,urlencode会编码为+,而 rawurlencode会编码为%20。一般情况下除了搜索引擎,我们的策略都是空格编码为%20。因此采用后者的居多。注意的是encode和 decode系列一定要配套使用。

strcmp系列函数
这一系列的函数包括strcmp、 strncmp、strcasecmp、strncasecmp,实现功能和C函数相同。但也有不同,由于php的字符串是允许\0出现,因此在判断的时候底层使用的是memcmp系列而非strcmp,理论上来说更快。另外由于php直接能获取到字符串长度,因此会首先这方面的检查,很多情况下效率就会高很多了。

is_int和is_numeric
这两个函数功能相似又不完全相同,使用的时候一定需要注意他们的区别。Is_int:判断一个变量类型是否是整数型,php变量中专门有一个字段表征类型,因此直接判断这个类型即可,是一个绝对 O(1)的操作 Is_numeric:判断一个变量是否是整数或数字字符串,也就是说除了整数型变量会返回true之外,对于字符串变量,如果形如”1234”,”1e4”等也会被判为true。这个时候会遍历字符串进行判断。

总结及建议

总结:
通过对函数实现的原理分析和性能测试,我们总结出以下一些结论
1. Php的函数调用开销相对较大。
2. 函数相关信息保存在一个大的hash_table中,每次调用时通过函数名在hash表中查找,因此函数名长度对性能也有一定影响。
3. 函数返回引用没有实际意义
4. 内置php函数性能比用户函数高很多,尤其对于字符串类操作。
5. 类方法、普通函数、静态方法效率几乎相同,没有太大差异
6. 除去空函数调用的影响,内置函数和同样功能的C函数性能基本差不多。
7. 所有的参数传递都是采用引用计数的浅拷贝,代价很小。
8. 函数个数对性能影响几乎可以忽略

建议:

因此,对于php函数的使用,有如下一些建议
1. 一个功能可以用内置函数完成,尽量使用它而不是自己编写php函数。
2. 如果某个功能对性能要求很高,可以考虑用扩展来实现。
3. Php函数调用开销较大,因此不要过分封装。有些功能,如果需要调用的次数很多本身又只用1、2行代码就行实现的,建议就不要封装调用了。
4. 不要过分迷恋各种设计模式,如上一条描述,过分的封装会带来性能的下降。需要考虑两者的权衡。Php有自己的特点,切不可东施效颦,过分效仿java的模式。
5. 函数不宜嵌套过深,递归使用要谨慎。
6. 伪函数性能很高,同等功能实现下优先考虑。比如用isset代替array_key_exists
7. 函数返回引用没有太大意义,也起不到实际作用,建议不予考虑。
8. 类成员方法效率不比普通函数低,因此不用担心性能损耗。建议多考虑静态方法,可读性及安全性都更好。
9. 如不是特殊需要,参数传递都建议使用传值而不是传引用。当然,如果参数是很大的数组且需要修改时可以考虑引用传递。

PHP 相关文章推荐
PHP生成带有雪花背景的验证码
Oct 09 PHP
用PHP实现小型站点广告管理(修正版)
Oct 09 PHP
php&java(三)
Oct 09 PHP
ThinkPHP模板自定义标签使用方法
Jun 26 PHP
兼容ie6浏览器的php下载文件代码分享
Jul 14 PHP
PHP面向对象精要总结
Nov 07 PHP
php使用parse_url和parse_str解析URL
Feb 22 PHP
浅谈PHP拦截器之__set()与__get()的理解与使用方法
Oct 18 PHP
php使用str_shuffle()函数生成随机字符串的方法分析
Feb 17 PHP
thinkphp3.2实现跨控制器调用其他模块的方法
Mar 14 PHP
asp函数split()对应php函数explode()
Feb 27 PHP
PHP上传图片到数据库并显示的实例代码
Dec 20 PHP
百度工程师讲PHP函数的实现原理及性能分析(二)
May 13 #PHP
百度工程师讲PHP函数的实现原理及性能分析(一)
May 13 #PHP
PHP版本如何选择?应该使用哪个版本?
May 13 #PHP
PHP Hash算法:Times33算法代码实例
May 13 #PHP
你应该知道PHP浮点数知识
May 13 #PHP
PHP浮点数精度问题汇总
May 13 #PHP
PHP生成器简单实例
May 13 #PHP
You might like
php懒人函数 自动添加数据
2011/06/28 PHP
PHP curl 并发最佳实践代码分享
2012/09/05 PHP
php 使用GD库为页面增加水印示例代码
2014/03/24 PHP
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
php格式文件打开的四种方法
2018/02/24 PHP
php判断/计算闰年的方法小结【三种方法】
2019/07/06 PHP
Yii框架Session与Cookie使用方法示例
2019/10/14 PHP
JavaScript的单例模式 (singleton in Javascript)
2010/06/11 Javascript
通过jQuery打造支持汉字,拼音,英文快速定位查询的超级select插件
2010/06/18 Javascript
基于jquery的兼容各种浏览器的iframe自适应高度的脚本
2010/08/13 Javascript
js文本框输入点回车触发确定兼容IE、FF等
2013/11/19 Javascript
JQUERY dialog的用法详细解析
2013/12/19 Javascript
javascript自定义函数参数传递为字符串格式
2014/07/29 Javascript
jquery获取input type=text中的值的各种方式(总结)
2016/12/02 Javascript
B/S(Web)实时通讯解决方案分享
2017/04/06 Javascript
js实现点击切换checkbox背景图片的简单实例
2017/05/08 Javascript
详解使用Next.js构建服务端渲染应用
2018/07/10 Javascript
Vue登录注册并保持登录状态的方法
2018/08/17 Javascript
jQuery 点击获取验证码按钮及倒计时功能
2018/09/20 jQuery
Nuxt项目支持eslint+pritter+typescript的实现
2019/05/20 Javascript
一文了解vue-router之hash模式和history模式
2019/05/31 Javascript
React Native登录之指纹登录篇的示例代码
2020/11/03 Javascript
python IDLE 背景以及字体大小的修改方法
2019/07/12 Python
Django Admin后台模型列表页面如何添加自定义操作按钮
2020/11/11 Python
Get The Label中文官网:英国运动时尚购物平台
2017/04/19 全球购物
澳大利亚和新西兰最大的在线旅行社之一:Aunt Betty
2019/08/07 全球购物
办公室前台岗位职责
2014/01/04 职场文书
会计电算化应届生自荐信
2014/02/25 职场文书
2014年社区学雷锋活动总结
2014/03/09 职场文书
安全生产责任书
2014/03/12 职场文书
搞笑婚礼主持词
2014/03/13 职场文书
意外伤害赔偿协议书范文
2014/09/23 职场文书
学生保证书
2015/01/16 职场文书
大学生暑期社会实践的个人总结!
2019/07/17 职场文书
Java 常见的限流算法详细分析并实现
2022/04/07 Java/Android
nginx设置资源请求目录的方式详解
2022/05/30 Servers