溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解PHP函數(shù)的實(shí)現(xiàn)原理及性能

發(fā)布時(shí)間:2021-09-29 10:03:14 來(lái)源:億速云 閱讀:146 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“如何理解PHP函數(shù)的實(shí)現(xiàn)原理及性能”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

php函數(shù)的分類

在php中,橫向劃分的話,函數(shù)分為兩大類: user function(內(nèi)置函數(shù)) 和internal function(內(nèi)置函數(shù))。前者就是用戶在程序中自定義的一些函數(shù)和方法,后者則是php本身提供的各類庫(kù)函數(shù)(比如sprintf、array_push等)。用戶也可以通過(guò)擴(kuò)展的方法來(lái)編寫庫(kù)函數(shù),這個(gè)將在后面介紹。對(duì)于user function,又可以細(xì)分為function(函數(shù))和method(類方法),本文中將就這三種函數(shù)分別進(jìn)行分析和測(cè)試。

php函數(shù)的實(shí)現(xiàn)

一個(gè)php函數(shù)最終是如何執(zhí)行,這個(gè)流程是怎么樣的呢?
要回答這個(gè)問(wèn)題,我們先來(lái)看看php代碼的執(zhí)行所經(jīng)過(guò)的流程。

如何理解PHP函數(shù)的實(shí)現(xiàn)原理及性能

從圖1可以看到,php實(shí)現(xiàn)了一個(gè)典型的動(dòng)態(tài)語(yǔ)言執(zhí)行過(guò)程:拿到一段代碼后,經(jīng)過(guò)詞法解析、語(yǔ)法解析等階段后,源程序會(huì)被翻譯成一個(gè)個(gè)指令(opcodes),然后ZEND虛擬機(jī)順次執(zhí)行這些指令完成操作。Php本身是用c實(shí)現(xiàn)的,因此最終調(diào)用的也都是c的函數(shù),實(shí)際上,我們可以把php看做是一個(gè)c開(kāi)發(fā)的軟件。通過(guò)上面描述不難看出,php中函數(shù)的執(zhí)行也是被翻譯成了opcodes來(lái)調(diào)用,每次函數(shù)調(diào)用實(shí)際上是執(zhí)行了一條或多條指令。

對(duì)于每一個(gè)函數(shù),zend都通過(guò)以下的數(shù)據(jù)結(jié)構(gòu)來(lái)描述

復(fù)制代碼 代碼如下:


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標(biāo)明了函數(shù)的類型:用戶函數(shù)、內(nèi)置函數(shù)、重載函數(shù)。Common中包含函數(shù)的基本信息,包括函數(shù)名,參數(shù)信息,函數(shù)標(biāo)志(普通函數(shù)、靜態(tài)方法、抽象方法)等內(nèi)容。另外,對(duì)于用戶函數(shù),還有一個(gè)函數(shù)符號(hào)表,記錄了內(nèi)部變量等,這個(gè)將在后面詳述。 Zend維護(hù)了一個(gè)全局function_table,這是一個(gè)大的hahs表。函數(shù)調(diào)用的時(shí)候會(huì)首先根據(jù)函數(shù)名從表中找到對(duì)應(yīng)的zend_function。當(dāng)進(jìn)行函數(shù)調(diào)用時(shí)候,虛擬機(jī)會(huì)根據(jù)type的不同決定調(diào)用方法, 不同類型的函數(shù),其執(zhí)行原理是不相同的 。

內(nèi)置函數(shù)

內(nèi)置函數(shù),其本質(zhì)上就是真正的c函數(shù),每一個(gè)內(nèi)置函數(shù),php在最終編譯后都會(huì)展開(kāi)成為一個(gè)名叫zif_xxxx的function,比如我們常見(jiàn)的sprintf,對(duì)應(yīng)到底層就是zif_sprintf。Zend在執(zhí)行的時(shí)候,如果發(fā)現(xiàn)是內(nèi)置函數(shù),則只是簡(jiǎn)單的做一個(gè)轉(zhuǎn)發(fā)操作。
Zend提供了一系列的api供調(diào)用,包括參數(shù)獲取、數(shù)組操作、內(nèi)存分配等。內(nèi)置函數(shù)的參數(shù)獲取,通過(guò)zend_parse_parameters方法來(lái)實(shí)現(xiàn),對(duì)于數(shù)組、字符串等參數(shù),zend實(shí)現(xiàn)的是淺拷貝,因此這個(gè)效率是很高的??梢赃@樣說(shuō),對(duì)于php內(nèi)置函數(shù),其效率和相應(yīng)c函數(shù)幾乎相同,唯一多了一次轉(zhuǎn)發(fā)調(diào)用。

內(nèi)置函數(shù)在php中都是通過(guò)so的方式進(jìn)行動(dòng)態(tài)加載,用戶也可以根據(jù)需要自己編寫相應(yīng)的so,也就是我們常說(shuō)的擴(kuò)展。ZEND提供了一系列的api供擴(kuò)展使用

用戶函數(shù)

和內(nèi)置函數(shù)相比,用戶通過(guò)php實(shí)現(xiàn)的自定義函數(shù)具有完全不同的執(zhí)行過(guò)程和實(shí)現(xiàn)原理。如前文所述,我們知道php代碼是被翻譯成為了一條條opcode來(lái)執(zhí)行的,用戶函數(shù)也不例外,實(shí)際中每個(gè)函數(shù)對(duì)應(yīng)到一組opcode,這組指令被保存在zend_function中。于是,用戶函數(shù)的調(diào)用最終就是對(duì)應(yīng)到一組opcodes的執(zhí)行。

》》局部變量的保存及遞歸的實(shí)現(xiàn)

我們知道,函數(shù)遞歸是通過(guò)堆棧來(lái)完成的。在php中,也是利用類似的方法來(lái)實(shí)現(xiàn)。Zend為每個(gè)php函數(shù)分配了一個(gè)活動(dòng)符號(hào)表(active_sym_table),記錄當(dāng)前函數(shù)中所有局部變量的狀態(tài)。所有的符號(hào)表通過(guò)堆棧的形式來(lái)維護(hù),每當(dāng)有函數(shù)調(diào)用的時(shí)候,分配一個(gè)新的符號(hào)表并入棧。當(dāng)調(diào)用結(jié)束后當(dāng)前符號(hào)表出棧。由此實(shí)現(xiàn)了狀態(tài)的保存和遞歸。
對(duì)于棧的維護(hù),zend在這里做了優(yōu)化。預(yù)先分配一個(gè)長(zhǎng)度為N的靜態(tài)數(shù)組來(lái)模擬堆棧,這種通過(guò)靜態(tài)數(shù)組來(lái)模擬動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)的手法在我們自己的程序中也經(jīng)常有使用,這種方式避免了每次調(diào)用帶來(lái)的內(nèi)存分配、銷毀。ZEND只是在函數(shù)調(diào)用結(jié)束時(shí)將當(dāng)前棧頂?shù)姆?hào)表數(shù)據(jù)clean掉即可。因?yàn)殪o態(tài)數(shù)組長(zhǎng)度為N,一旦函數(shù)調(diào)用層次超過(guò)N,程序不會(huì)出現(xiàn)棧溢出,這種情況下zend就會(huì)進(jìn)行符號(hào)表的分配、銷毀,因此會(huì)導(dǎo)致性能下降很多。在zend里面,N目前取值是32。因此,我們編寫php程序的時(shí)候,函數(shù)調(diào)用層次最好不要超過(guò)32。當(dāng)然,如果是web應(yīng)用,本身可以函數(shù)調(diào)用層次的深度。

》》參數(shù)的傳遞 和內(nèi)置函數(shù)調(diào)用zend_parse_params來(lái)獲取參數(shù)不同,用戶函數(shù)中參數(shù)的獲取是通過(guò)指令來(lái)完成的。函數(shù)有幾個(gè)參數(shù)就對(duì)應(yīng)幾條指令。具體到實(shí)現(xiàn)上就是普通的變量賦值。通過(guò)上面的分析可以看出,和內(nèi)置函數(shù)相比,由于是自己維護(hù)堆棧表,而且每條指令的執(zhí)行也是一個(gè)c函數(shù),用戶函數(shù)的性能相對(duì)會(huì)差很多,后面會(huì)有具體的對(duì)比分析。因此,如果一個(gè)功能有對(duì)應(yīng)php內(nèi)置函數(shù)實(shí)現(xiàn)的盡量不要自己重新寫函數(shù)去實(shí)現(xiàn)。

“如何理解PHP函數(shù)的實(shí)現(xiàn)原理及性能”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

php
AI