The goal of the new memory manager (available since PHP 5.2) is to reduce memory allocation overhead and speedup memory management.
Normal:
sapi/cli/php -r 'leak();'
Zend MM disabled:
USE_ZEND_ALLOC=0 valgrind --leak-check=full sapi/cli/php -r 'leak();'
Since PHP 5.3.11 it is possible to prevent shared extensions from
unloading so that valgrind can correctly track the memory leaks in
shared extensions. For this there is the
ZEND_DONT_UNLOAD_MODULES
environment variable. If set, then
DL_UNLOAD()
is skipped during the shutdown of shared
extensions.
ZEND_VM
architecture allows specializing opcode handlers
according to op_type
fields and using different execution
methods (call threading, switch threading and direct threading). As a
result ZE2 got more than 20% speedup on raw PHP code execution (with
specialized executor and direct threading execution method). As in most
PHP applications raw execution speed isn't the limiting factor but
system calls and database calls are, your mileage with this patch will
vary.
Most parts of the old zend_execute.c go into
zend_vm_def.h
. Here you can find opcode handlers and
helpers. The typical opcode handler template looks like this:
(<OPCODE-NUMBER>, <OPCODE>, <OP1_TYPES>, <OP2_TYPES>)
ZEND_VM_HANDLER{
<HANDLER'S CODE>
}
<OPCODE-NUMBER>
is a opcode number (0, 1, ...)
<OPCODE>
is an opcode name (ZEN_NOP, ZEND_ADD, :)
<OP1_TYPES>
and <OP2_TYPES>
are
masks for allowed operand op_types. Specializer will generate code only
for defined combination of types. You can use any combination of the
following op_types UNUSED, CONST, VAR, TMP and CV also you can use ANY
mask to disable specialization according operand's op_type.
<HANDLER'S CODE>
is a handler's code itself. For most
handlers it stills the same as in old zend_execute.c
, but
now it uses macros to access opcode operands and some internal executor
data.
You can see the conformity of new macros to old code in the following list:
EXECUTE_DATA
execute_data(<OP>)
ZEND_VM_DISPATCH_TO_HANDLERreturn <OP>_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
(<NAME>)
ZEND_VM_DISPATCH_TO_HELPERreturn <NAME>(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
(<NAME>,<PARAM>,<VAL>)
ZEND_VM_DISPATCH_TO_HELPER_EXreturn <NAME>(<VAL>, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
()
ZEND_VM_CONTINUEreturn 0
()
ZEND_VM_NEXT_OPCODE()
NEXT_OPCODE(<TARGET>
ZEND_VM_SET_OPCODE(<TARGET>
SET_OPCODE()
ZEND_VM_INC_OPCODE()
INC_OPCOD()
ZEND_VM_RETURN_FROM_EXECUTE_LOOP()
RETURN_FROM_EXECUTE_LOOP(<LABEL>):
ZEND_VM_C_LABEL<LABEL>:
(<LABEL>)
ZEND_VM_C_GOTOgoto <LABEL>
<X>_TYPE
OP->op<X>.op_type
opline<X>_ZVAL_PTR(<TYPE>)
GET_OP(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
get_zval_ptr<X>_ZVAL_PTR_PTR(<TYPE>)
GET_OP(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
get_zval_ptr_ptr<X>_OBJ_ZVAL_PTR(<TYPE>)
GET_OP(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
get_obj_zval_ptr<X>_OBJ_ZVAL_PTR_PTR(<TYPE>)
GET_OP(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
get_obj_zval_ptr_ptr<X>_TMP_FREE()
IS_OP(free_op<X>)
IS_TMP_FREE<X>()
FREE_OP(free_op<X>)
FREE_OP<X>_IF_VAR()
FREE_OP(free_op<X>)
FREE_VAR<X>_VAR_PTR()
FREE_OP(free_op<X>) FREE_VAR_PTR
Executor's helpers can be defined without parameters or with one parameter. This is done with the following constructs:
(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>)
ZEND_VM_HELPER{
<HELPER'S CODE>
}
(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>, <PARAM_SPEC>)
ZEND_VM_HELPER_EX{
<HELPER'S CODE>
}
Executor's code is generated by PHP script zend_vm_gen.php it uses
zend_vm_def.h
and zend_vm_execute.skl
as input
and produces zend_vm_opcodes.h
and
zend_vm_execute.h
. The first file is a list of opcode
definitions. It is included from zend_compile.h
. The second
one is an executor code itself. It is included from
zend_execute.c
.
zend_vm_gen.php
can produce different kind of executors.
You can select different opcode threading model using
--with-vm-kind=CALL|SWITCH|GOTO
. You can disable opcode
specialization using --without-specializer
. You can include
or exclude old executor together with specialized one using
--without-old-executor
. At last you can debug executor
using original zend_vm_def.h
or generated file
zend_vm_execute.h
. Debugging with original file requires
--with-lines
option. By default ZE2 uses the following
command to generate executor:
php zend_vm_gen.php --with-vm-kind=CALL