mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
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:
parent
f74a02d263
commit
8ffc20ace6
2 changed files with 56 additions and 48 deletions
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue