]> granicus.if.org Git - php/commitdiff
Speed up foreach/FE_FREE (optimize for arrays without gc)
authorTyson Andre <tysonandre775@hotmail.com>
Sat, 16 Nov 2019 02:29:46 +0000 (21:29 -0500)
committerDmitry Stogov <dmitry@zend.com>
Mon, 18 Nov 2019 08:54:35 +0000 (11:54 +0300)
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;
}
```

Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index 0750a02db8b72114a5f2b2ec9693701915cd53d9..c6d37e57a0d9642945659868e02af7de01999a61 100644 (file)
@@ -3044,13 +3044,24 @@ ZEND_VM_HOT_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
        zval *var;
        USE_OPLINE
 
-       SAVE_OPLINE();
        var = EX_VAR(opline->op1.var);
-       if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
-               zend_hash_iterator_del(Z_FE_ITER_P(var));
+       if (Z_TYPE_P(var) != IS_ARRAY) {
+               SAVE_OPLINE();
+               if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+                       zend_hash_iterator_del(Z_FE_ITER_P(var));
+               }
+               zval_ptr_dtor_nogc(var);
+               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        }
-       zval_ptr_dtor_nogc(var);
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+
+       /* This is freeing an array. Use an inlined version of zval_ptr_dtor_nogc. */
+       /* 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) */
+       if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) {
+               SAVE_OPLINE();
+               rc_dtor_func(Z_COUNTED_P(var));
+               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+       }
+       ZEND_VM_NEXT_OPCODE();
 }
 
 ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
index d1ef53128f6968231e626cd36ae992c74fdcbeec..7e173dc2f70996275498a73df9e1c1bb666ebf8d 100644 (file)
@@ -12882,13 +12882,24 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVA
        zval *var;
        USE_OPLINE
 
-       SAVE_OPLINE();
        var = EX_VAR(opline->op1.var);
-       if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
-               zend_hash_iterator_del(Z_FE_ITER_P(var));
+       if (Z_TYPE_P(var) != IS_ARRAY) {
+               SAVE_OPLINE();
+               if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+                       zend_hash_iterator_del(Z_FE_ITER_P(var));
+               }
+               zval_ptr_dtor_nogc(var);
+               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        }
-       zval_ptr_dtor_nogc(var);
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+
+       /* This is freeing an array. Use an inlined version of zval_ptr_dtor_nogc. */
+       /* 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) */
+       if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) {
+               SAVE_OPLINE();
+               rc_dtor_func(Z_COUNTED_P(var));
+               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+       }
+       ZEND_VM_NEXT_OPCODE();
 }
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)