diff options
| author | Nikita Popov <nikic@php.net> | 2012-05-22 23:14:36 +0200 |
|---|---|---|
| committer | Nikita Popov <nikic@php.net> | 2012-05-22 23:17:59 +0200 |
| commit | 5e763d9420cbccbd8ee4f14a263b2439e6c5ae88 (patch) | |
| tree | 958dbb49d8094a6a3188ba0ef58a4dc92c76cf3c | |
| parent | 46fa26ab853b766c9222e49dbe7555de360b42f4 (diff) | |
| download | php-git-5e763d9420cbccbd8ee4f14a263b2439e6c5ae88.tar.gz | |
Allocate execute_data using malloc for generators
Generators need to switch the execute_data very often. If the execute_data
is allocated on the VM stack this operation would require to always copy
the structure (which is quite large). That's why the execution context is
allocated on the heap instead (only for generators obviously).
| -rw-r--r-- | Zend/zend_vm_def.h | 8 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.h | 44 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.skl | 36 |
3 files changed, 78 insertions, 10 deletions
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 13ae824c48..20b5633b8a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2530,7 +2530,13 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) nested = EX(nested); - zend_vm_stack_free(execute_data TSRMLS_CC); + /* For generators the execute_data is stored on the heap, for everything + * else it is stored on the VM stack. */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + efree(execute_data); + } else { + zend_vm_stack_free(execute_data TSRMLS_CC); + } if (nested) { execute_data = EG(current_execute_data); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 12338117f5..d351067cb7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -342,11 +342,15 @@ static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* o ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { DCL_OPLINE + zend_execute_data *execute_data; + size_t execute_data_size; + zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); + if (EG(exception)) { return; } @@ -354,11 +358,35 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = 1; zend_vm_enter: - /* Initialize execute_data */ - execute_data = (zend_execute_data *)zend_vm_stack_alloc( + /* + * When allocating the execute_data, memory for compiled variables and + * temporary variables is also allocated after the actual zend_execute_data + * struct. op_array->last_var specifies the number of compiled variables and + * op_array->T is the number of temporary variables. If there is no symbol + * table, then twice as much memory is allocated for compiled variables. + * In that case the first half contains zval**s and the second half the + * actual zval*s (which would otherwise be in the symbol table). + */ + execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T + ; + + /* + * Normally the execute_data is allocated on the VM stack (because it does + * not actually do any allocation and thus is faster). For generators + * though this behavior would be suboptimal, because the (rather large) + * structure would have to be copied back and forth every time execution is + * suspended or resumed. That's why for generators the execution context + * is allocated using emalloc, thus allowing to save and restore it simply + * by replacing a pointer. + */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + execute_data = emalloc(execute_data_size); + } else { + execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + } EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); @@ -477,7 +505,13 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) nested = EX(nested); - zend_vm_stack_free(execute_data TSRMLS_CC); + /* For generators the execute_data is stored on the heap, for everything + * else it is stored on the VM stack. */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + efree(execute_data); + } else { + zend_vm_stack_free(execute_data TSRMLS_CC); + } if (nested) { execute_data = EG(current_execute_data); diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 426f689795..359e052008 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -3,9 +3,13 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) { DCL_OPLINE + zend_execute_data *execute_data; + size_t execute_data_size; + zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); + {%HELPER_VARS%} {%INTERNAL_LABELS%} @@ -17,11 +21,35 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = 1; zend_vm_enter: - /* Initialize execute_data */ - execute_data = (zend_execute_data *)zend_vm_stack_alloc( + /* + * When allocating the execute_data, memory for compiled variables and + * temporary variables is also allocated after the actual zend_execute_data + * struct. op_array->last_var specifies the number of compiled variables and + * op_array->T is the number of temporary variables. If there is no symbol + * table, then twice as much memory is allocated for compiled variables. + * In that case the first half contains zval**s and the second half the + * actual zval*s (which would otherwise be in the symbol table). + */ + execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T + ; + + /* + * Normally the execute_data is allocated on the VM stack (because it does + * not actually do any allocation and thus is faster). For generators + * though this behavior would be suboptimal, because the (rather large) + * structure would have to be copied back and forth every time execution is + * suspended or resumed. That's why for generators the execution context + * is allocated using emalloc, thus allowing to save and restore it simply + * by replacing a pointer. + */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + execute_data = emalloc(execute_data_size); + } else { + execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + } EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); |
