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


Posted in PHP onMay 13, 2015

前言

在任何语言中,函数都是最基本的组成单元。对于php的函数,它具有哪些特点?函数调用是怎么实现的?php函数的性能如何,有什么使用建议?本文将从原理出发进行分析结合实际的性能测试尝试对这些问题进行回答,在了解实现的同时更好的编写php程序。同时也会对一些常见的php函数进行介绍。

php函数的分类

在php中,横向划分的话,函数分为两大类: user function(内置函数) 和internal function(内置函数)。前者就是用户在程序中自定义的一些函数和方法,后者则是php本身提供的各类库函数(比如sprintf、array_push等)。用户也可以通过扩展的方法来编写库函数,这个将在后面介绍。对于user function,又可以细分为function(函数)和method(类方法),本文中将就这三种函数分别进行分析和测试。

php函数的实现

一个php函数最终是如何执行,这个流程是怎么样的呢?
要回答这个问题,我们先来看看php代码的执行所经过的流程。

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

从图1可以看到,php实现了一个典型的动态语言执行过程:拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令(opcodes),然后ZEND虚拟机顺次执行这些指令完成操作。Php本身是用c实现的,因此最终调用的也都是c的函数,实际上,我们可以把php看做是一个c开发的软件。通过上面描述不难看出,php中函数的执行也是被翻译成了opcodes来调用,每次函数调用实际上是执行了一条或多条指令。

对于每一个函数,zend都通过以下的数据结构来描述

typedef union _zend_function { 

zend_uchar type; /* MUST be the first element of this struct! */ 

struct { 

zend_uchar type; /* never used */ 

char *function_name; 

zend_class_entry *scope; 

zend_uint fn_flags; 

union _zend_function *prototype; 

zend_uint num_args; 

zend_uint required_num_args; 

zend_arg_info *arg_info; 

zend_bool pass_rest_by_reference; 

unsigned char return_reference; 

} common;
zend_op_array op_array; 

zend_internal_function internal_function; 

} zend_function;


typedef struct _zend_function_state { 

HashTable *function_symbol_table; 

zend_function *function; 

void *reserved[ZEND_MAX_RESERVED_RESOURCES]; 

} zend_function_state;

其中type标明了函数的类型:用户函数、内置函数、重载函数。Common中包含函数的基本信息,包括函数名,参数信息,函数标志(普通函数、静态方法、抽象方法)等内容。另外,对于用户函数,还有一个函数符号表,记录了内部变量等,这个将在后面详述。 Zend维护了一个全局function_table,这是一个大的hahs表。函数调用的时候会首先根据函数名从表中找到对应的zend_function。当进行函数调用时候,虚拟机会根据type的不同决定调用方法, 不同类型的函数,其执行原理是不相同的 。

内置函数

内置函数,其本质上就是真正的c函数,每一个内置函数,php在最终编译后都会展开成为一个名叫zif_xxxx的function,比如我们常见的sprintf,对应到底层就是zif_sprintf。Zend在执行的时候,如果发现是内置函数,则只是简单的做一个转发操作。
Zend提供了一系列的api供调用,包括参数获取、数组操作、内存分配等。内置函数的参数获取,通过zend_parse_parameters方法来实现,对于数组、字符串等参数,zend实现的是浅拷贝,因此这个效率是很高的。可以这样说,对于php内置函数,其效率和相应c函数几乎相同,唯一多了一次转发调用。

内置函数在php中都是通过so的方式进行动态加载,用户也可以根据需要自己编写相应的so,也就是我们常说的扩展。ZEND提供了一系列的api供扩展使用

用户函数

和内置函数相比,用户通过php实现的自定义函数具有完全不同的执行过程和实现原理。如前文所述,我们知道php代码是被翻译成为了一条条opcode来执行的,用户函数也不例外,实际中每个函数对应到一组opcode,这组指令被保存在zend_function中。于是,用户函数的调用最终就是对应到一组opcodes的执行。

》》局部变量的保存及递归的实现

我们知道,函数递归是通过堆栈来完成的。在php中,也是利用类似的方法来实现。Zend为每个php函数分配了一个活动符号表(active_sym_table),记录当前函数中所有局部变量的状态。所有的符号表通过堆栈的形式来维护,每当有函数调用的时候,分配一个新的符号表并入栈。当调用结束后当前符号表出栈。由此实现了状态的保存和递归。
对于栈的维护,zend在这里做了优化。预先分配一个长度为N的静态数组来模拟堆栈,这种通过静态数组来模拟动态数据结构的手法在我们自己的程序中也经常有使用,这种方式避免了每次调用带来的内存分配、销毁。ZEND只是在函数调用结束时将当前栈顶的符号表数据clean掉即可。因为静态数组长度为N,一旦函数调用层次超过N,程序不会出现栈溢出,这种情况下zend就会进行符号表的分配、销毁,因此会导致性能下降很多。在zend里面,N目前取值是32。因此,我们编写php程序的时候,函数调用层次最好不要超过32。当然,如果是web应用,本身可以函数调用层次的深度。

》》参数的传递 和内置函数调用zend_parse_params来获取参数不同,用户函数中参数的获取是通过指令来完成的。函数有几个参数就对应几条指令。具体到实现上就是普通的变量赋值。通过上面的分析可以看出,和内置函数相比,由于是自己维护堆栈表,而且每条指令的执行也是一个c函数,用户函数的性能相对会差很多,后面会有具体的对比分析。因此,如果一个功能有对应php内置函数实现的尽量不要自己重新写函数去实现。

PHP 相关文章推荐
BBS(php & mysql)完整版(八)
Oct 09 PHP
聊天室php&mysql(二)
Oct 09 PHP
探讨PHP函数ip2long转换IP时数值太大产生负数的解决方法
Jun 06 PHP
php中jpgraph类库的使用介绍
Aug 08 PHP
修改apache配置文件去除thinkphp url中的index.php
Jan 17 PHP
PHP Reflection API详解
May 12 PHP
PHP基于phpqrcode生成带LOGO图像的二维码实例
Jul 10 PHP
深入理解PHP原理之执行周期分析
Jun 01 PHP
JavaScript实现删除电脑的关机键
Jul 26 PHP
Laravel 5.3 学习笔记之 配置
Aug 28 PHP
PHP7.0连接DB操作实例分析【基于mysqli】
Sep 26 PHP
laravel dingo API返回自定义错误信息的实例
Sep 29 PHP
PHP版本如何选择?应该使用哪个版本?
May 13 #PHP
PHP Hash算法:Times33算法代码实例
May 13 #PHP
你应该知道PHP浮点数知识
May 13 #PHP
PHP浮点数精度问题汇总
May 13 #PHP
PHP生成器简单实例
May 13 #PHP
php实现比较两个字符串日期大小的方法
May 12 #PHP
php使用substr()和strpos()联合查找字符串中某一特定字符的方法
May 12 #PHP
You might like
Mac OS下配置PHP+MySql环境
2015/02/25 PHP
php为字符串前后添加指定数量字符的方法
2015/05/04 PHP
PHP用PDO如何封装简单易用的DB类详解
2017/07/30 PHP
Laravel5.1 框架Request请求操作常见用法实例分析
2020/01/04 PHP
JavaScript 三种不同位置代码的写法
2009/10/25 Javascript
js 获取Listbox选择的值的代码
2010/04/15 Javascript
基于jQuery的动态表格插件
2011/03/28 Javascript
判断多个input type=file是否有已经选择好文件的代码
2012/05/23 Javascript
Extjs中ComboBoxTree实现的下拉框树效果(自写)
2013/05/28 Javascript
jquery 获取 outerHtml 包含当前节点本身的代码
2014/10/30 Javascript
jQuery 和 CSS 的文本特效插件集锦
2014/12/12 Javascript
Jquery注册事件实现方法
2015/05/18 Javascript
谈谈我对JavaScript中typeof和instanceof的深入理解
2015/12/25 Javascript
Webpack 实现 AngularJS 的延迟加载
2016/03/02 Javascript
[原创]JavaScript语法高亮插件highlight.js用法详解【附highlight.js本站下载】
2016/11/01 Javascript
BootStrap Select清除选中的状态恢复默认状态
2017/06/20 Javascript
Vue中定义全局变量与常量的各种方式详解
2017/08/23 Javascript
three.js中3D视野的缩放实现代码
2017/11/16 Javascript
jQuery实现导航样式布局操作示例【可自定义样式布局】
2018/07/24 jQuery
React 无状态组件(Stateless Component) 与高阶组件
2018/08/14 Javascript
详解React中合并单元格的正确写法
2019/01/08 Javascript
jquery实现直播弹幕效果
2019/11/28 jQuery
JavaScript交换变量常用4种方法解析
2020/09/02 Javascript
Python3.5模块的定义、导入、优化操作图文详解
2019/04/27 Python
python实现京东订单推送到测试环境,提供便利操作示例
2019/08/09 Python
Python3搭建http服务器的实现代码
2020/02/11 Python
python爬虫学习笔记之Beautifulsoup模块用法详解
2020/04/09 Python
使用OpenCV校准鱼眼镜头的方法
2020/11/26 Python
一款纯css3实现的非常实用的鼠标悬停特效演示
2014/11/05 HTML / CSS
详解css3 Transition属性(平滑过渡菜单栏案例)
2017/09/05 HTML / CSS
HTML5 LocalStorage 本地存储刷新值还在
2017/03/10 HTML / CSS
会计系中文个人求职信
2013/12/24 职场文书
《果园机器人》教学反思
2014/04/13 职场文书
政府个人对照检查材料
2014/08/28 职场文书
2016元旦主持人经典开场白台词
2015/12/03 职场文书
python自动获取微信公众号最新文章的实现代码
2022/07/15 Python