Skip to content

Commit 7901913

Browse files
TysonAndredstogov
authored andcommitted
Speed up foreach/FE_FREE (optimize for arrays without gc)
In the case where there are still references to an array being iterated over when the iterator is freed (or the array is not reference counted): - There's need to save the opline. - There's no need to check for exceptions. ``` // Before: 0.404 seconds // After: 0.362 seconds // loop_iter_empty(1000, 5000); function loop_iter_empty(int $a, int $b) { $values = array_fill(0, $b, []); $total = 0; for ($i = 0; $i < $b; $i++) { foreach ($values as $v) { foreach ($v as $x) { $total += $x; } } } return $total; } ```
1 parent 8bf663d commit 7901913

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

Zend/zend_vm_def.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,13 +3044,24 @@ ZEND_VM_HOT_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
30443044
zval *var;
30453045
USE_OPLINE
30463046

3047-
SAVE_OPLINE();
30483047
var = EX_VAR(opline->op1.var);
3049-
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
3050-
zend_hash_iterator_del(Z_FE_ITER_P(var));
3048+
if (Z_TYPE_P(var) != IS_ARRAY) {
3049+
SAVE_OPLINE();
3050+
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
3051+
zend_hash_iterator_del(Z_FE_ITER_P(var));
3052+
}
3053+
zval_ptr_dtor_nogc(var);
3054+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
30513055
}
3052-
zval_ptr_dtor_nogc(var);
3053-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
3056+
3057+
/* This is freeing an array. Use an inlined version of zval_ptr_dtor_nogc. */
3058+
/* PHP only needs to save the opline and check for an exception if the last reference to the array was garbage collected (destructors of elements in the array could throw an exception) */
3059+
if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) {
3060+
SAVE_OPLINE();
3061+
rc_dtor_func(Z_COUNTED_P(var));
3062+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
3063+
}
3064+
ZEND_VM_NEXT_OPCODE();
30543065
}
30553066

30563067
ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV)

Zend/zend_vm_execute.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12882,13 +12882,24 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVA
1288212882
zval *var;
1288312883
USE_OPLINE
1288412884

12885-
SAVE_OPLINE();
1288612885
var = EX_VAR(opline->op1.var);
12887-
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
12888-
zend_hash_iterator_del(Z_FE_ITER_P(var));
12886+
if (Z_TYPE_P(var) != IS_ARRAY) {
12887+
SAVE_OPLINE();
12888+
if (Z_FE_ITER_P(var) != (uint32_t)-1) {
12889+
zend_hash_iterator_del(Z_FE_ITER_P(var));
12890+
}
12891+
zval_ptr_dtor_nogc(var);
12892+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
1288912893
}
12890-
zval_ptr_dtor_nogc(var);
12891-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
12894+
12895+
/* This is freeing an array. Use an inlined version of zval_ptr_dtor_nogc. */
12896+
/* PHP only needs to save the opline and check for an exception if the last reference to the array was garbage collected (destructors of elements in the array could throw an exception) */
12897+
if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) {
12898+
SAVE_OPLINE();
12899+
rc_dtor_func(Z_COUNTED_P(var));
12900+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
12901+
}
12902+
ZEND_VM_NEXT_OPCODE();
1289212903
}
1289312904

1289412905
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

0 commit comments

Comments
 (0)