Preface
In any language, functions are the most basic building blocks. What are the characteristics of PHP functions? How is function calling implemented? How is the performance of the php function? Any suggestions on how to use it? This article will try to answer these questions by analyzing the principles and combining them with actual performance tests, so as to better write PHP programs while understanding the implementation. At the same time, some common PHP functions will be introduced.
Classification of PHP functions
In PHP, if divided horizontally, functions are divided into two categories: user function (built-in function) and internal function. The former are some functions and methods customized by users in the program, and the latter are various library functions provided by PHP itself (such as sprintf, array_push, etc.). Users can also write library functions through extension methods, which will be introduced later. For user function, it can be subdivided into function (function) and method (class method). In this article, these three functions will be analyzed and tested respectively.
Recommended tutorial: PHP video tutorial
##Implementation of php function
How is a PHP function ultimately executed? What is the process like? To answer this question, let’s first take a look at the process of executing the PHP code. As you can see from the picture, PHP implements a typical dynamic language execution process: after getting a piece of code, after going through stages such as lexical analysis and syntax analysis, the source code The program will be translated into instructions (opcodes), and then the ZEND virtual machine will execute these instructions in sequence to complete the operation. Php itself is implemented in C, so the functions ultimately called are all C functions. In fact, we can regard PHP as a software developed in C. It is not difficult to see from the above description that the execution of functions in PHP is also translated into opcodes for calling. Each function call actually executes one or more instructions. For each function, zend is described through the following data structuretypedef 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;
Built-in functions
Built-in functions are essentially real C functions. For each built-in function, PHP will expand it after final compilation. It becomes a function named zif_xxxx, such as our common sprintf, which corresponds to the bottom layer is zif_sprintf. When Zend is executing, if it finds a built-in function, it simply performs a forwarding operation. Zend provides a series of APIs for calling, including parameter acquisition, array operations, memory allocation, etc. The parameters of the built-in function are obtained through the zend_parse_parameters method. For parameters such as arrays and strings, zend implements shallow copying, so this efficiency is very high. It can be said that for PHP built-in functions, their efficiency is almost the same as that of the corresponding C functions, with the only additional forwarding call. Built-in functions are dynamically loaded in PHP through so. Users can also write corresponding so according to their needs, which is what we often call extensions. ZEND provides a series of APIs for extension useUser functions
Compared with built-in functions, user-defined functions implemented through PHP Have completely different execution processes and implementation principles. As mentioned above, we know that PHP code is translated into opcodes for execution, and user functions are no exception. In fact, each function corresponds to a set of opcodes, and this set of instructions is saved in zend_function. Therefore, the call to the user function ultimately corresponds to the execution of a set of opcodes.Save local variables and implement recursion
We know that function recursion is completed through the stack. In php, a similar method is used to achieve this. Zend assigns an active symbol table (active_sym_table) to each PHP function to record the status of all local variables in the current function. All symbol tables are maintained in the form of a stack. Whenever a function is called, a new symbol table is allocated and pushed onto the stack. When the call ends, the current symbol table is popped off the stack. This enables state preservation and recursion.
For stack maintenance, zend has made optimizations here. Pre-allocate a static array of length N to simulate the stack. This method of simulating dynamic data structures through static arrays is often used in our own programs. This method avoids the memory allocation caused by each call. destroy. ZEND just cleans the symbol table data on the top of the current stack at the end of the function call.
Because the static array length is N, once the function call level exceeds N, the program will not cause stack overflow. In this case, zend will allocate and destroy the symbol table, which will cause a lot of performance degradation. In zend, the current value of N is 32. Therefore, when we write PHP programs, it is best not to exceed 32 function call levels. Of course, if it is a web application, the function call level itself can be deep.
Transfer of parameters
Different from the built-in function calling zend_parse_params to obtain parameters, the acquisition of parameters in user functions is completed through instructions. How many parameters a function has corresponds to how many instructions it has. Specific to implementation, it is ordinary variable assignment.
It can be seen from the above analysis that compared with the built-in functions, since the stack table is maintained by itself, and the execution of each instruction is also a C function, the performance of the user function will be relatively much worse, as will be discussed later Specific comparative analysis. Therefore, if a function has a corresponding PHP built-in function, try not to rewrite the function yourself to implement it.
Class method
The execution principle of class method is the same as that of user function, and it is also translated into opcodes and called sequentially. Class implementation is implemented by zend using a data structure zend_class_entry, which stores some basic information related to the class. This entry is processed when PHP is compiled.
In the common of zend_function, there is a member called scope, which points to the zend_class_entry of the class corresponding to the current method. Regarding the object-oriented implementation in PHP, I will not give a more detailed introduction here. In the future, I will write a special article to detail the object-oriented implementation principle in PHP. As far as the function is concerned, the implementation principle of method is exactly the same as that of function, and its performance is similar in theory. We will make a detailed performance comparison later.
The impact of function name length on performance
Test method
For name length 1 , 2, 4, 8, and 16 functions, test and compare the number of times they can be executed per second, and determine the impact of function name length on performance
The test results are as shown below
Result Analysis
As can be seen from the figure, the length of the function name will still have a certain impact on performance. A function of length 1 and an empty function call of length 16 have a performance difference of 1x. It is not difficult to find the reason by analyzing the source code. As mentioned above, when a function is called, zend will first query relevant information by function name in a global function_table, which is a hash table. Inevitably, the longer the name, the more time it takes to query. Therefore, when actually writing a program, it is recommended that the name of a function that is called multiple times should not be too long
Although the length of a function name has a certain impact on performance, how big is it specifically? This issue should still be considered based on the actual situation. If a function itself is relatively complex, it will not have a big impact on the overall performance.
One suggestion is to give concise and concise names to functions that will be called many times and have relatively simple functions.
The impact of the number of functions on performance
Testing method
In the following three environments Perform function call test below and analyze the results: 1. The program contains only 1 function 2. The program contains 100 functions 3. The program contains 1000 functions.
Test the number of functions that can be called per second in these three situations
The test results are as shown below
Result Analysis
It can be seen from the test results that the performance in these three cases is almost the same. When the number of functions increases, the performance decrease is minimal and can be ignored.
From the analysis of implementation principles, the only difference between several implementations is the function acquisition part. As mentioned before, all functions are placed in a hash table, and the search efficiency should still be close to O(1) under different numbers, so the performance difference is not big.
Consumption of different types of function calls
Test method
Select user function, class method, There is one type of static method and built-in function. The function itself does not do anything and returns directly. It mainly tests the consumption of empty function calls. The test results are the number of executions per second
In order to remove other effects during the test, all function names have the same length
The test results are as shown below
Result Analysis
It can be seen from the test results that for the PHP functions written by users themselves, no matter what type they are, their efficiency is almost the same. About 280w/s. As we expected, even for air conditioners, the efficiency of the built-in function is much higher, reaching 780w/s, which is three times that of the former. It can be seen that the overhead of built-in function calls is still much lower than that of user functions. From the previous principle analysis, it can be seen that the main gap lies in operations such as initializing the symbol table and receiving parameters when the user function is called.
Performance comparison between built-in functions and user functions
Testing method
Built-in functions and user functions For performance comparison, here we select several commonly used functions, and then use PHP functions to implement the same functions for performance comparison.
In the test, we selected a typical example from string, mathematics, and array for comparison. These functions are string interception (substr), decimal to binary conversion (decbin), and minimum value (min) and returns all keys in the array (array_keys).
The test results are as shown below
Result analysis
It can be seen from the test results It turns out that, as we expected, the overall performance of built-in functions is much higher than that of ordinary user functions. Especially for functions involving string operations, the gap reaches 1 order of magnitude. Therefore, one principle for using functions is that if a certain function has a corresponding built-in function, try to use it instead of writing the PHP function yourself.
For some functions involving a large number of string operations, in order to improve performance, you can consider using extensions to implement them. For example, common rich text filtering, etc.
Comparison with C function performance
Testing method
We select three functions each for string operation and arithmetic operation for comparison. PHP uses extension accomplish. The three functions are simple one-time arithmetic operations, string comparisons, and multiple arithmetic operations.
In addition to the two types of functions, we will also test the performance after removing the function air conditioning overhead. On the one hand, we compare the performance differences of the two functions (C and PHP built-in), and on the other hand, we verify the air conditioning from the side. Function consumption
The test point is the time consumption of executing 100,000 operations
The test results are as shown below
Result Analysis
The difference between the cost of built-in functions and C functions is small after removing the impact of php function air conditioning. As the functions become more and more complex, the performance of both parties approaches the same. This can be easily demonstrated from the previous function implementation analysis. After all, the built-in functions are implemented in C.
The more complex the function, the smaller the performance gap between C and PHP
Compared with C, the overhead of PHP function calls is much higher, and the performance of simple functions still has a certain impact. Therefore, functions in PHP should not be nested and encapsulated too deeply.
Pseudo functions and their performance
In PHP, there are some functions, which are standard function usage, but the underlying The implementation is completely different from real function calls. These functions do not belong to any of the three functions mentioned above. Their essence is a separate opcode, which is called a pseudo function or instruction function here.
As mentioned above, pseudo functions are used just like standard functions and appear to have the same characteristics. But when they are finally executed, they are reflected by zend into a corresponding instruction (opcode) for calling, so their implementation is closer to operations such as if, for, and arithmetic operations.
Pseudo functions in php
1. isset
2. empty
3. unset
4.eval
From the above introduction, it can be seen that since pseudo functions are directly translated into instructions for execution, compared with ordinary functions, there is one less overhead caused by a function call, so the performance will be better. We make a comparison through the following test. Both Array_key_exists and isset can determine whether a key exists in the array. Take a look at their performance
As can be seen from the figure, compared with array_key_exists, isset performance It is much higher, basically about 4 times that of the former, and even compared with empty function calls, its performance is about 1 times higher. This also proves that the overhead of PHP function calls is still relatively large.
The above is the detailed content of PHP function principle. For more information, please follow other related articles on the PHP Chinese website!