In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly shows you "how to optimize recursion in PHP7", the content is easy to understand, clear, hope to help you solve your doubts, let the editor lead you to study and learn "how to optimize recursion in PHP7" this article.
⒈ recursion
recursion is often used in programming because of its simplicity and elegance. Recursive code is more declarative and self-descriptive. Recursion does not need to explain how to get the value like iteration, but rather describes the final result of the function.
takes the implementation of the accumulation and Fibonacci series as an example:
Iterative implementation
/ / accumulation function / / given parameter n, find the sum of positive integers less than or equal to n function sumBelow (int $n) {if ($n type)
< ZEND_USER_FUNCTION)) { /* 内部方法调用 */ /* ... ... */ } else { /* ZEND_OVERLOADED_FUNCTION */ /* 重载的方法 */ /* ... ... */ }fcall_end: /* 异常判断以及相应的后续处理 */ /* ... ... */ zend_vm_stack_free_call_frame(call); /* 异常判断以及相应的后续处理 */ /* ... ... */ ZEND_VM_SET_OPCODE(opline + 1); ZEND_VM_CONTINUE();} 从 DO_FCALL 的底层实现可以看出,当发生方法递归调用时(zend_execute_ex == execute_ex),ZEND_VM_ENTER() 宏将 execute_data 转换为当前方法的 execute_data ,同时将 opline 又置为 execute_data 中的第一条指令,在检查完异常(ZEND_VM_INTERRUPT_CHECK())之后,返回然后重新执行方法。 通过蹦床函数的方式优化递归调用主要应用在对象的魔术方法 __call 、__callStatic 中。 class A{ private function test($n) { echo "test $n", PHP_EOL; } public function __call($method, $args) { $this->$method (. $args); var_export ($this); echo PHP_EOL;}} class B extends A {public function _ call ($method, $args) {(new parent)-> $method (. $args); var_export ($this); echo PHP_EOL;} class C extends B {public function _ call ($method, $args) {(new parent)-> $method (. $args) Var_export ($this); echo PHP_EOL;}} $c = new C (); / / $c-> test (11); echo memory_get_peak_usage (), PHP_EOL;// tests show that the peak memory consumed by initializing only $c objects is 402416 bytes, and the peak memory consumed by calling the test method is 431536 bytes
When attempts to call a method in an object, if the method does not exist in the current object or access is restricted (protected, private), the object's magic method _ _ call is called (and _ _ callStatic is called if called statically). In the underlying implementation of PHP, this process is implemented through the zend_std_get_method function.
Static union _ zend_function * zend_std_get_method (zend_object * * obj_ptr, zend_string * method_name, const zval * key) {zend_object * zobj = * obj_ptr; zval * func; zend_function * fbc; zend_string * lc_method_name; zend_class_entry * scope = NULL; ALLOCA_FLAG (use_heap) If (EXPECTED (key! = NULL)) {lc_method_name = Z_STR_P (key); # ifdef ZEND_ALLOCA_MAX_SIZE use_heap = 0 ZSTR_ALLOCA_ALLOC (lc_method_name, ZSTR_LEN (method_name), use_heap) Zend_str_tolower_copy (ZSTR_VAL (lc_method_name), ZSTR_VAL (method_name), ZSTR_LEN (method_name)) } / * the method called does not exist in the current object * / if (UNEXPECTED ((func = zend_hash_find (& zobj- > ce- > function_table, lc_method_name)) = = NULL) {if (UNEXPECTED (! key)) {ZSTR_ALLOCA_FREE (lc_method_name, use_heap) } if (zobj- > ce- > _ _ call) {/ * Magic method exists in the current object _ _ call * / return zend_get_user_call_function (zobj- > ce, method_name);} else {return NULL Static zend_always_inline zend_function * zend_get_user_call_function (zend_class_entry * ce, zend_string * method_name) {return zend_get_call_trampoline_func (ce, method_name, 0) if the method called is of type protected or private. } ZEND_API zend_function * zend_get_call_trampoline_func (zend_class_entry * ce, zend_string * method_name, int is_static) {size_t mname_len; zend_op_array * func; zend_function * fbc = is_static? Ce- > _ _ callstatic: ce- > _ call; ZEND_ASSERT (fbc); if (EXPECTED (EG (trampoline). Common.function_name = = NULL)) {func = & EG (trampoline). Op_array;} else {func = ecalloc (1, sizeof (zend_op_array));} func- > type = ZEND_USER_FUNCTION Func- > arg_flags [0] = 0; func- > arg_flags [1] = 0; func- > arg_flags [2] = 0; func- > fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC; if (is_static) {func- > fn_flags | = ZEND_ACC_STATIC;} func- > opcodes = & EG (call_trampoline_op) Func- > prototype = fbc; func- > scope = fbc- > common.scope; / * reserve space for arguments, local and temorary variables * / func- > T = (fbc- > type = = ZEND_USER_FUNCTION)? MAX (fbc- > op_array.last_var + fbc- > op_array.T, 2): 2; func- > filename = (fbc- > type = = ZEND_USER_FUNCTION)? Fbc- > op_array.filename: ZSTR_EMPTY_ALLOC (); func- > line_start = (fbc- > type = = ZEND_USER_FUNCTION)? Fbc- > op_array.line_start: 0; func- > line_end = (fbc- > type = = ZEND_USER_FUNCTION)? Fbc- > op_array.line_end: 0; / /? Keep compatibility for "\ 0" characters / /? See: Zend/tests/bug46238.phpt if ((mname_len = strlen (ZSTR_VAL (method_name)! = ZSTR_LEN (method_name)) {func- > function_name = zend_string_init (ZSTR_VAL (method_name), mname_len, 0);} else {func- > function_name = zend_string_copy (method_name) } return (zend_function*) func;} static void zend_init_call_trampoline_op (void) {memset (& EG (call_trampoline_op), 0, sizeof (EG (call_trampoline_op); EG (call_trampoline_op). Opcode = ZEND_CALL_TRAMPOLINE; EG (call_trampoline_op). Op1_type = IS_UNUSED; EG (call_trampoline_op). Op2_type = IS_UNUSED EG (call_trampoline_op). Result_type = IS_UNUSED; ZEND_VM_SET_OPCODE_HANDLER (& EG (call_trampoline_op);}
The underlying implementation logic of ZEND_CALL_TRAMPOLINE:
Static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER (ZEND_OPCODE_HANDLER_ARGS) {zend_array * args; zend_function * fbc = EX (func); zval * ret = EX (return_value); uint32_t call_info = EX_CALL_INFO () & (ZEND_CALL_NESTED | ZEND_CALL_TOP | ZEND_CALL_RELEASE_THIS); uint32_t num_args = EX_NUM_ARGS () Zend_execute_data * call; USE_OPLINE args = emalloc (sizeof (zend_array)); zend_hash_init (args, num_args, NULL, ZVAL_PTR_DTOR, 0); if (num_args) {zval * p = ZEND_CALL_ARG (execute_data, 1); zval * end = p + num_args Zend_hash_real_init (args, 1); ZEND_HASH_FILL_PACKED (args) {do {ZEND_HASH_FILL_ADD (p); while (p! = end) } ZEND_HASH_FILL_END ();} SAVE_OPLINE (); call = execute_data; execute_data = EG (current_execute_data) = EX (prev_execute_data); ZEND_ASSERT (zend_vm_calc_used_stack (2, fbc- > common.prototype) func = fbc- > common.prototype; ZEND_CALL_NUM_ARGS (call) = 2 ZVAL_STR (ZEND_CALL_ARG (call, 1), fbc- > common.function_name); ZVAL_ARR (ZEND_CALL_ARG (call, 2), args); zend_free_trampoline (fbc); fbc = call- > func If (EXPECTED (fbc- > type = = ZEND_USER_FUNCTION) {if (UNEXPECTED (! fbc- > op_array.run_time_cache)) {init_func_run_time_cache (& fbc- > op_array);} i_init_func_execute_data (call, & fbc- > op_array, ret) If (EXPECTED (zend_execute_ex = = execute_ex)) {ZEND_VM_ENTER ();} else {ZEND_ADD_CALL_FLAG (call, ZEND_CALL_TOP); zend_execute_ex (call) }} else {/ *.... * /} / *.. * /}
can see from the underlying implementation of ZEND_CALL_TRAMPOLINE that when a recursive call to _ _ call occurs (the call to _ _ call occurs successively in class C, class B, and class An in the above example), ZEND_VM_ENTER transforms execute_data and opline, and then executes it again.
needs to be returned after recursion, and the returned function is implemented in RETURN. After all the PHP code has been compiled into OPCode, the last OPCode instruction must be RETURN (even if there is no return in the code, it will be automatically added at compile time). In ZEND_RETURN, the last step is zend_leave_helper, and the recursive return is done immediately at this step.
# define LOAD_NEXT_OPLINE () opline = EX (opline) + "define ZEND_VM_CONTINUE () return# define ZEND_VM_LEAVE () ZEND_VM_CONTINUE () static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC (ZEND_OPCODE_HANDLER_ARGS) {zend_execute_data * old_execute_data; uint32_t call_info = EX_CALL_INFO () If (EXPECTED ((call_info & (ZEND_CALL_CODE | ZEND_CALL_TOP | ZEND_CALL_HAS_SYMBOL_TABLE | ZEND_CALL_FREE_EXTRA_ARGS | ZEND_CALL_ALLOCATED)) = = 0) {/ *... * / LOAD_NEXT_OPLINE (); ZEND_VM_LEAVE () } else if (EXPECTED ((call_info & (ZEND_CALL_CODE | ZEND_CALL_TOP)) = = 0)) {i_free_compiled_variables (execute_data); if (UNEXPECTED (call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {zend_clean_and_cache_symbol_table (EX (symbol_table)) } EG (current_execute_data) = EX (prev_execute_data); / *... * / zend_vm_stack_free_extra_args_ex (call_info, execute_data); old_execute_data = execute_data; execute_data = EX (prev_execute_data) Zend_vm_stack_free_call_frame_ex (call_info, old_execute_data); if (UNEXPECTED (EG (exception)! = NULL)) {const zend_op * old_opline = EX (opline); zend_throw_exception_internal (NULL) If (RETURN_VALUE_USED (old_opline)) {zval_ptr_dtor (EX_VAR (old_opline- > result.var));} HANDLE_EXCEPTION_LEAVE ();} LOAD_NEXT_OPLINE () ZEND_VM_LEAVE ();} else if (EXPECTED ((call_info & ZEND_CALL_TOP) = = 0)) {/ *... * / LOAD_NEXT_OPLINE (); ZEND_VM_LEAVE ();} else {/ *.... * /}}
in zend_leave_helper, execute_data is replaced with prev_execute_data, and then continues to execute the new execute_data 's opline. (note: instead of initializing opline to the first OPCode of opline in execute_data, the next OPCode is executed at the location where it was previously executed.)
The above is all the content of the article "how to optimize recursion in PHP7". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.