What is NOPNOP? It is a special opcode that indicates an empty operation. It exists in many places, and the meaning of NOP in the Assembly is the same. The null operation in the machine instruction is usually used to align the memory address, to improve the efficiency of CPU access to memory, GCC and other compilers will also optimize specific statements and generate null operations. OpcodeNOP: ZEND_NOP in PHP
What is NOP? It is a special opcode that indicates null operations. It exists in many places, and the meaning of NOP in assembly is the same. The null operations in machine commands are usually used to align the memory address, to improve the efficiency of CPU access to memory, GCC and other compilers will also optimize specific statements and generate null operations. Opcode NOP: ZEND_NOP in PHP
What is NOP?
NOP is a special opcode that indicates null operations. It exists in many places, and the meaning of NOP in assembly is the same. The null operations in machine commands are usually used to align memory addresses, to improve the efficiency of CPU access to memory, GCC and other compilers will also optimize specific statements and generate null operations.
Opcode NOP: ZEND_NOP in PHP
PHP is based on the Zend Virtual Machine. Other virtual machine-based languages generally have commands similar to NOP. The PHP documentation provides a simple description of this:
/* VLD output result */line # op fetch ext return operands6 0 NOP 7 1 RETURN 1 Function name: ACompiled variables: noneline # op fetch ext return operands6 0 RETURN null
The above VLD results show that the declaration of function A () is changedNOP
Operation.
Zend virtual machine is an advanced abstraction. do not consider the issue of memory alignment. Why do I still need to perform such operations with null?
The reason is simply the result of compilation optimization. Some content can be determined during compilation (called Early Binding of Early Binding), so some Opcodes can be replaced with null during compilation.
Let's take a look at the compilation result of this Code:
> JMPZ true, ->3 3 1 > ZEND_DECLARE_CLASS $0 '%00foo%2FUsers%2Freeze%2FOpensource%2Fphp-test%2Fphp-src-5.4%2Ftest.php0x106cd601f', 'foo' 4 2 > JMP ->3 6 3 > ZEND_FETCH_CLASS 4 :1 'Bar' 4 NEW :1 5 DO_FCALL_BY_NAME 0 7 6 NOP 9 7 > RETURN
Like the previous official function definition code, the opcode of the class Bar is changed to NOP in the 7th lines output by VLD. However, please note that the opcode defined by this class Foo is ZEND_DECLARE_CLASS, that is, the declaration of the class.
Why is the OPCODE of the first class declaration different from that of the second class declaration?
The code in the above example can be executed before the Bar class declaration.new Bar
But the Bar class declaration is not executed at this time. Why can I access the Bar class,This is because the Bar class declaration has been completed at compilation.Because the Bar class has been declared during compilation, you do not need to execute the Declaration class operation again during actual execution, so the opcode corresponding to it is replaced with NOP.
The Foo class is in a condition-based judgment block. during compilation, it cannot be determined whether the Foo class will be executed. Therefore, it still needs to be declared during execution, so opcode has not changed.
For code that uses the opcode cache, moving the declaration of functions and classes to the compilation reduces opcode execution during execution, which can speed up code execution.
Optimization
You may wonder why this opcode cannot be directly discarded since the Declaration of classes and functions can be optimized? One can reduce opcode usage. For example, many frameworks have a large number of class definitions, which can also reduce the execution time, because null operations are not zero cost, CPU consumption is still required when NOP is executed.
Let's take a look at an important function in the opcode compilation process:
zend_op *get_next_op(zend_op_array *op_array TSRMLS_DC){ zend_uint next_op_num = op_array->last++; zend_op *next_op; if (next_op_num >= CG(context).opcodes_size) { if (op_array->fn_flags & ZEND_ACC_INTERACTIVE) { /* we messed up */ zend_printf("Ran out of opcode space!\n" "You should probably consider writing this huge script into a file!\n"); zend_bailout(); } CG(context).opcodes_size *= 4; op_array_alloc_ops(op_array, CG(context).opcodes_size); } next_op = &(op_array->opcodes[next_op_num]); init_op(next_op TSRMLS_CC); return next_op;}
This function returns a zend_op (the smallest unit of opcode) each time. The opcode bucket application is similar to that of the hash table. The pre-applied space is used. If the space is insufficient, it can be resized properly. During compilation, the opcode is in file units, and the number of function or class declarations in a file is usually not too large. During compilation, the opcode array is already pre-applied. Therefore, this opcode can be optimized in a timely manner, and the actual memory usage during compilation will not be optimized.
Currently, only the ZEND_NOP opcode is used in a few cases. Readers can refer to Zend/zend_compile.c: zend_do_early_binding (). This function is used to determine the function and class declaration that can be determined during compilation, after the function or class declaration is completed, set the current compiled opcode to ZEND_NOP. This is because the function or class needs to be declared again after execution.
NOTEOf course, not all global classes or methods are bound early. For details, refer to the implementation of Zend_compile.c file mentioned above.
This function can actually optimize the generated ZEND_NOP. For example, the opcode is optimized in the eAccelarator extension, and ZEND_NOP is removed from the opcode_array array, because the opcode cache extension optimization is only performed once, And the execution is performed multiple times, this optimization is worthwhile. Currently, the Zend engine has not been optimized. First of all, from the code perspective, the number of classes and function declarations is significantly different from the number of other commands, therefore, at least the benefits of Optimization in this region are limited. To ensure the simplicity of the Zend engine, it is not optimized.
Currently, the APC extension is basically determined to be an official extension of the default PHP opcode cache, so these optimizations can be carried out in the extension to ensure that the Zend engine is easy to maintain.
Original article address: NOP in PHP and why does this opcode exist? Thanks for sharing it with the original author.