手机版

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

时间:2021-11-06 来源:互联网 编辑:宝哥软件园 浏览:

在任何语言中,函数都是最基本的单位。php的函数有什么特点?函数调用是如何实现的?php函数的性能如何,有什么建议?本文将从原理上进行分析,并结合实际的性能测试尝试回答这些问题,以便在理解实现的同时更好地编写php程序。它还介绍了一些常见的php函数。

php函数的分类。

在php中,横向上,函数分为两类:用户函数和内部函数。前者是程序中一些用户自定义的函数和方法,后者是php自己提供的各种库函数(如sprintf、array_push等)。).用户还可以通过扩展方法编写库函数,这将在后面介绍。用户功能可细分为功能和方法,本文将分别对其进行分析和测试。

php函数的实现。

一个php函数最终是如何执行的,过程是怎样的?为了回答这个问题,让我们首先看看php代码执行的流程。

从图1可以看出,php实现了一个典型的动态语言执行过程:获取一段代码后,经过词法解析和语法解析,将源程序翻译成操作码,然后ZEND虚拟机依次执行这些指令完成操作。Php本身是用c实现的,所以c的所有功能最终都被调用了。其实我们可以把php看作是c开发的软件,从上面的描述中不难看出,php中函数的执行也被翻译成操作码进行调用,每个函数调用实际上都执行一条或多条指令。

对于每个函数,zend通过以下数据结构描述复制的代码: typedef union _ Zend _ function { Zend _ uchartype;/*必须是此结构的第一个元素!*/struct { zend_uchar类型;/*从未使用过*/char * function _ name;zend _ class _ entry *作用域;zend _ uint fn _ flagsunion _ zend _ function *原型;zend _ uint num _ argszend _ uint required _ num _ argszend _ arg _ info * arg _ infoZend _ bool pass _ rest _ by _ reference;无符号字符return _ reference}常见;

zend _ op _ array op _ arrayZend _ internal _ function internal _ function;} zend _ function

typedef struct _ Zend _ function _ state { HashTable * function _ symbol _ table;zend _ function *函数;void * RESERVED[ZEND _ MAX _ RESERVED _ RESOURCES];} zend _ function _ state

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

内置功能

内置函数本质上是实C函数。在php中最终编译后,每个内置函数都将被扩展成一个名为zif_xxxx的函数。例如,我们常见的sprintf对应于底部的zif_sprintf。Zend执行时,如果发现是内置函数,就简单做一个转发操作。Zend提供了一系列用于调用的API,包括参数获取、数组操作、内存分配等。内置函数的参数获取通过zend_parse_parameters方法实现。对于数组、字符串等参数,zend实现了浅拷贝,所以这个效率很高。可以说,php内置函数的效率和对应的C函数差不多,只多了一个转发调用。

内置函数以so的方式在php中动态加载,用户也可以根据需要编写相应的so,这就是我们常说的扩展。ZEND提供了一系列用于扩展的API。

用户功能

与内置函数相比,php实现的用户自定义函数有着完全不同的执行过程和实现原则。如上所述,我们知道php代码被翻译成操作码来执行,用户函数也不例外。事实上,每个函数对应一组操作码,这组指令保存在zend_function中。因此,用户函数的调用最终对应于一组操作码的执行。

局部变量的保存和递归的实现。

众所周知,函数递归是通过栈完成的。在php中,它也以类似的方式实现。Zend为每个php函数分配一个活动符号表(active_sym_table),该表记录当前函数中所有局部变量的状态。所有符号表都以堆栈的形式维护。每当有函数调用时,都会分配一个新的符号表并将其合并到堆栈中。当调用结束时,当前符号表被弹出。从而实现了状态保存和递归。Zend在这里优化了堆栈维护。预分配一个长度为n的静态数组来模拟堆栈在我们自己的程序中经常用到,避免了每次调用造成的内存分配和破坏。ZEND只需在函数调用结束时清除当前堆栈顶部的符号表数据。因为静态数组的长度是n,一旦函数调用级别超过n,程序就不会有堆栈溢出。在这种情况下,zend会分配并销毁符号表,这将导致大量性能下降。在zend中,n的当前值是32。因此,当我们编写php程序时,函数调用级别最好不要超过32。当然,如果它是一个web应用程序,它本身可以调用层次结构的深度。

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

版权声明:百度工程师谈PHP函数的实现原理和性能分析(一)是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。