Optimize Traversable unpacking in zend_vm_def.h

The C compiler sees that a dynamic function is being called, so it cannot infer
that iter->funcs has not changed.

This results in more assembly instructions and slightly more time to execute that code
path.

Unpacking traversables to arrays(`ZEND_ADD_ARRAY_UNPACK`),
starting foreach loops (`ZEND_FE_FETCH*`), etc. are affected.

```
<?php
/*
 * Before: 1.576 seconds
 * After:  1.474 seconds
 */
function example() {
    $start = hrtime(true);
    $it = new SplFixedArray(1000);
    $total = 0;
    for ($i = 0; $i < 100000; $i++) {
        $total += count([...$it]);
    }
    $end = hrtime(true);
    printf("Elapsed: %.6f\n", ($end - $start) / 1_000_000_000);
}
example();
```
This commit is contained in:
Tyson Andre 2021-02-12 19:42:03 -05:00
parent f74a02d263
commit 8ffc20ace6
2 changed files with 56 additions and 48 deletions

View file

@ -5092,26 +5092,27 @@ ZEND_VM_C_LABEL(send_again):
HANDLE_EXCEPTION();
}
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
const zend_object_iterator_funcs *funcs = iter->funcs;
if (funcs->rewind) {
funcs->rewind(iter);
}
for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
for (; funcs->valid(iter) == SUCCESS; ++arg_num) {
zval *arg, *top;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
arg = iter->funcs->get_current_data(iter);
arg = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
zend_string *name = NULL;
if (iter->funcs->get_current_key) {
if (funcs->get_current_key) {
zval key;
iter->funcs->get_current_key(iter, &key);
funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
@ -5173,7 +5174,7 @@ ZEND_VM_C_LABEL(send_again):
ZEND_CALL_NUM_ARGS(EX(call))++;
}
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
}
zend_iterator_dtor(iter);
@ -5997,25 +5998,26 @@ ZEND_VM_C_LABEL(add_unpack_again):
HANDLE_EXCEPTION();
}
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
const zend_object_iterator_funcs *funcs = iter->funcs;
if (funcs->rewind) {
funcs->rewind(iter);
}
for (; iter->funcs->valid(iter) == SUCCESS; ) {
for (; funcs->valid(iter) == SUCCESS; ) {
zval *val;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
val = iter->funcs->get_current_data(iter);
val = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
zval key;
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, &key);
if (funcs->get_current_key) {
funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
@ -6045,7 +6047,7 @@ ZEND_VM_C_LABEL(add_unpack_again):
}
}
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
if (UNEXPECTED(EG(exception))) {
break;
}
@ -6748,15 +6750,16 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
}
}
} else {
const zend_object_iterator_funcs *funcs = iter->funcs;
if (EXPECTED(++iter->index > 0)) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@ -6765,7 +6768,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
ZEND_VM_C_GOTO(fe_fetch_r_exit);
}
}
value = iter->funcs->get_current_data(iter);
value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@ -6775,8 +6778,8 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
ZEND_VM_C_GOTO(fe_fetch_r_exit);
}
if (RETURN_VALUE_USED(opline)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (funcs->get_current_key) {
funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@ -6901,15 +6904,16 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
}
}
} else {
const zend_object_iterator_funcs *funcs = iter->funcs;
if (++iter->index > 0) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@ -6918,7 +6922,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
ZEND_VM_C_GOTO(fe_fetch_w_exit);
}
}
value = iter->funcs->get_current_data(iter);
value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@ -6928,8 +6932,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
ZEND_VM_C_GOTO(fe_fetch_w_exit);
}
if (RETURN_VALUE_USED(opline)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (funcs->get_current_key) {
funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();

View file

@ -2203,26 +2203,27 @@ send_again:
HANDLE_EXCEPTION();
}
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
const zend_object_iterator_funcs *funcs = iter->funcs;
if (funcs->rewind) {
funcs->rewind(iter);
}
for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
for (; funcs->valid(iter) == SUCCESS; ++arg_num) {
zval *arg, *top;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
arg = iter->funcs->get_current_data(iter);
arg = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
zend_string *name = NULL;
if (iter->funcs->get_current_key) {
if (funcs->get_current_key) {
zval key;
iter->funcs->get_current_key(iter, &key);
funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
@ -2284,7 +2285,7 @@ send_again:
ZEND_CALL_NUM_ARGS(EX(call))++;
}
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
}
zend_iterator_dtor(iter);
@ -2544,25 +2545,26 @@ add_unpack_again:
HANDLE_EXCEPTION();
}
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
const zend_object_iterator_funcs *funcs = iter->funcs;
if (funcs->rewind) {
funcs->rewind(iter);
}
for (; iter->funcs->valid(iter) == SUCCESS; ) {
for (; funcs->valid(iter) == SUCCESS; ) {
zval *val;
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
val = iter->funcs->get_current_data(iter);
val = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
zval key;
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, &key);
if (funcs->get_current_key) {
funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
@ -2592,7 +2594,7 @@ add_unpack_again:
}
}
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
if (UNEXPECTED(EG(exception))) {
break;
}
@ -21692,15 +21694,16 @@ fe_fetch_r_exit:
}
}
} else {
const zend_object_iterator_funcs *funcs = iter->funcs;
if (EXPECTED(++iter->index > 0)) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@ -21709,7 +21712,7 @@ fe_fetch_r_exit:
goto fe_fetch_r_exit;
}
}
value = iter->funcs->get_current_data(iter);
value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@ -21719,8 +21722,8 @@ fe_fetch_r_exit:
goto fe_fetch_r_exit;
}
if (RETURN_VALUE_USED(opline)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (funcs->get_current_key) {
funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@ -21845,15 +21848,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
}
}
} else {
const zend_object_iterator_funcs *funcs = iter->funcs;
if (++iter->index > 0) {
/* This could cause an endless loop if index becomes zero again.
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
@ -21862,7 +21866,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
goto fe_fetch_w_exit;
}
}
value = iter->funcs->get_current_data(iter);
value = funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@ -21872,8 +21876,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
goto fe_fetch_w_exit;
}
if (RETURN_VALUE_USED(opline)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (funcs->get_current_key) {
funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();