Skip to content

Commit 8ffc20a

Browse files
committed
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(); ```
1 parent f74a02d commit 8ffc20a

File tree

2 files changed

+56
-48
lines changed

2 files changed

+56
-48
lines changed

Zend/zend_vm_def.h

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5092,26 +5092,27 @@ ZEND_VM_C_LABEL(send_again):
50925092
HANDLE_EXCEPTION();
50935093
}
50945094

5095-
if (iter->funcs->rewind) {
5096-
iter->funcs->rewind(iter);
5095+
const zend_object_iterator_funcs *funcs = iter->funcs;
5096+
if (funcs->rewind) {
5097+
funcs->rewind(iter);
50975098
}
50985099

5099-
for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
5100+
for (; funcs->valid(iter) == SUCCESS; ++arg_num) {
51005101
zval *arg, *top;
51015102

51025103
if (UNEXPECTED(EG(exception) != NULL)) {
51035104
break;
51045105
}
51055106

5106-
arg = iter->funcs->get_current_data(iter);
5107+
arg = funcs->get_current_data(iter);
51075108
if (UNEXPECTED(EG(exception) != NULL)) {
51085109
break;
51095110
}
51105111

51115112
zend_string *name = NULL;
5112-
if (iter->funcs->get_current_key) {
5113+
if (funcs->get_current_key) {
51135114
zval key;
5114-
iter->funcs->get_current_key(iter, &key);
5115+
funcs->get_current_key(iter, &key);
51155116
if (UNEXPECTED(EG(exception) != NULL)) {
51165117
break;
51175118
}
@@ -5173,7 +5174,7 @@ ZEND_VM_C_LABEL(send_again):
51735174
ZEND_CALL_NUM_ARGS(EX(call))++;
51745175
}
51755176

5176-
iter->funcs->move_forward(iter);
5177+
funcs->move_forward(iter);
51775178
}
51785179

51795180
zend_iterator_dtor(iter);
@@ -5997,25 +5998,26 @@ ZEND_VM_C_LABEL(add_unpack_again):
59975998
HANDLE_EXCEPTION();
59985999
}
59996000

6000-
if (iter->funcs->rewind) {
6001-
iter->funcs->rewind(iter);
6001+
const zend_object_iterator_funcs *funcs = iter->funcs;
6002+
if (funcs->rewind) {
6003+
funcs->rewind(iter);
60026004
}
60036005

6004-
for (; iter->funcs->valid(iter) == SUCCESS; ) {
6006+
for (; funcs->valid(iter) == SUCCESS; ) {
60056007
zval *val;
60066008

60076009
if (UNEXPECTED(EG(exception) != NULL)) {
60086010
break;
60096011
}
60106012

6011-
val = iter->funcs->get_current_data(iter);
6013+
val = funcs->get_current_data(iter);
60126014
if (UNEXPECTED(EG(exception) != NULL)) {
60136015
break;
60146016
}
60156017

60166018
zval key;
6017-
if (iter->funcs->get_current_key) {
6018-
iter->funcs->get_current_key(iter, &key);
6019+
if (funcs->get_current_key) {
6020+
funcs->get_current_key(iter, &key);
60196021
if (UNEXPECTED(EG(exception) != NULL)) {
60206022
break;
60216023
}
@@ -6045,7 +6047,7 @@ ZEND_VM_C_LABEL(add_unpack_again):
60456047
}
60466048
}
60476049

6048-
iter->funcs->move_forward(iter);
6050+
funcs->move_forward(iter);
60496051
if (UNEXPECTED(EG(exception))) {
60506052
break;
60516053
}
@@ -6748,15 +6750,16 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
67486750
}
67496751
}
67506752
} else {
6753+
const zend_object_iterator_funcs *funcs = iter->funcs;
67516754
if (EXPECTED(++iter->index > 0)) {
67526755
/* This could cause an endless loop if index becomes zero again.
67536756
* In case that ever happens we need an additional flag. */
6754-
iter->funcs->move_forward(iter);
6757+
funcs->move_forward(iter);
67556758
if (UNEXPECTED(EG(exception) != NULL)) {
67566759
UNDEF_RESULT();
67576760
HANDLE_EXCEPTION();
67586761
}
6759-
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
6762+
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
67606763
/* reached end of iteration */
67616764
if (UNEXPECTED(EG(exception) != NULL)) {
67626765
UNDEF_RESULT();
@@ -6765,7 +6768,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
67656768
ZEND_VM_C_GOTO(fe_fetch_r_exit);
67666769
}
67676770
}
6768-
value = iter->funcs->get_current_data(iter);
6771+
value = funcs->get_current_data(iter);
67696772
if (UNEXPECTED(EG(exception) != NULL)) {
67706773
UNDEF_RESULT();
67716774
HANDLE_EXCEPTION();
@@ -6775,8 +6778,8 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
67756778
ZEND_VM_C_GOTO(fe_fetch_r_exit);
67766779
}
67776780
if (RETURN_VALUE_USED(opline)) {
6778-
if (iter->funcs->get_current_key) {
6779-
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
6781+
if (funcs->get_current_key) {
6782+
funcs->get_current_key(iter, EX_VAR(opline->result.var));
67806783
if (UNEXPECTED(EG(exception) != NULL)) {
67816784
UNDEF_RESULT();
67826785
HANDLE_EXCEPTION();
@@ -6901,15 +6904,16 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
69016904
}
69026905
}
69036906
} else {
6907+
const zend_object_iterator_funcs *funcs = iter->funcs;
69046908
if (++iter->index > 0) {
69056909
/* This could cause an endless loop if index becomes zero again.
69066910
* In case that ever happens we need an additional flag. */
6907-
iter->funcs->move_forward(iter);
6911+
funcs->move_forward(iter);
69086912
if (UNEXPECTED(EG(exception) != NULL)) {
69096913
UNDEF_RESULT();
69106914
HANDLE_EXCEPTION();
69116915
}
6912-
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
6916+
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
69136917
/* reached end of iteration */
69146918
if (UNEXPECTED(EG(exception) != NULL)) {
69156919
UNDEF_RESULT();
@@ -6918,7 +6922,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
69186922
ZEND_VM_C_GOTO(fe_fetch_w_exit);
69196923
}
69206924
}
6921-
value = iter->funcs->get_current_data(iter);
6925+
value = funcs->get_current_data(iter);
69226926
if (UNEXPECTED(EG(exception) != NULL)) {
69236927
UNDEF_RESULT();
69246928
HANDLE_EXCEPTION();
@@ -6928,8 +6932,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
69286932
ZEND_VM_C_GOTO(fe_fetch_w_exit);
69296933
}
69306934
if (RETURN_VALUE_USED(opline)) {
6931-
if (iter->funcs->get_current_key) {
6932-
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
6935+
if (funcs->get_current_key) {
6936+
funcs->get_current_key(iter, EX_VAR(opline->result.var));
69336937
if (UNEXPECTED(EG(exception) != NULL)) {
69346938
UNDEF_RESULT();
69356939
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,26 +2203,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
22032203
HANDLE_EXCEPTION();
22042204
}
22052205

2206-
if (iter->funcs->rewind) {
2207-
iter->funcs->rewind(iter);
2206+
const zend_object_iterator_funcs *funcs = iter->funcs;
2207+
if (funcs->rewind) {
2208+
funcs->rewind(iter);
22082209
}
22092210

2210-
for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
2211+
for (; funcs->valid(iter) == SUCCESS; ++arg_num) {
22112212
zval *arg, *top;
22122213

22132214
if (UNEXPECTED(EG(exception) != NULL)) {
22142215
break;
22152216
}
22162217

2217-
arg = iter->funcs->get_current_data(iter);
2218+
arg = funcs->get_current_data(iter);
22182219
if (UNEXPECTED(EG(exception) != NULL)) {
22192220
break;
22202221
}
22212222

22222223
zend_string *name = NULL;
2223-
if (iter->funcs->get_current_key) {
2224+
if (funcs->get_current_key) {
22242225
zval key;
2225-
iter->funcs->get_current_key(iter, &key);
2226+
funcs->get_current_key(iter, &key);
22262227
if (UNEXPECTED(EG(exception) != NULL)) {
22272228
break;
22282229
}
@@ -2284,7 +2285,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
22842285
ZEND_CALL_NUM_ARGS(EX(call))++;
22852286
}
22862287

2287-
iter->funcs->move_forward(iter);
2288+
funcs->move_forward(iter);
22882289
}
22892290

22902291
zend_iterator_dtor(iter);
@@ -2544,25 +2545,26 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_UNPACK_SPEC_HANDLER(
25442545
HANDLE_EXCEPTION();
25452546
}
25462547

2547-
if (iter->funcs->rewind) {
2548-
iter->funcs->rewind(iter);
2548+
const zend_object_iterator_funcs *funcs = iter->funcs;
2549+
if (funcs->rewind) {
2550+
funcs->rewind(iter);
25492551
}
25502552

2551-
for (; iter->funcs->valid(iter) == SUCCESS; ) {
2553+
for (; funcs->valid(iter) == SUCCESS; ) {
25522554
zval *val;
25532555

25542556
if (UNEXPECTED(EG(exception) != NULL)) {
25552557
break;
25562558
}
25572559

2558-
val = iter->funcs->get_current_data(iter);
2560+
val = funcs->get_current_data(iter);
25592561
if (UNEXPECTED(EG(exception) != NULL)) {
25602562
break;
25612563
}
25622564

25632565
zval key;
2564-
if (iter->funcs->get_current_key) {
2565-
iter->funcs->get_current_key(iter, &key);
2566+
if (funcs->get_current_key) {
2567+
funcs->get_current_key(iter, &key);
25662568
if (UNEXPECTED(EG(exception) != NULL)) {
25672569
break;
25682570
}
@@ -2592,7 +2594,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_UNPACK_SPEC_HANDLER(
25922594
}
25932595
}
25942596

2595-
iter->funcs->move_forward(iter);
2597+
funcs->move_forward(iter);
25962598
if (UNEXPECTED(EG(exception))) {
25972599
break;
25982600
}
@@ -21692,15 +21694,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
2169221694
}
2169321695
}
2169421696
} else {
21697+
const zend_object_iterator_funcs *funcs = iter->funcs;
2169521698
if (EXPECTED(++iter->index > 0)) {
2169621699
/* This could cause an endless loop if index becomes zero again.
2169721700
* In case that ever happens we need an additional flag. */
21698-
iter->funcs->move_forward(iter);
21701+
funcs->move_forward(iter);
2169921702
if (UNEXPECTED(EG(exception) != NULL)) {
2170021703
UNDEF_RESULT();
2170121704
HANDLE_EXCEPTION();
2170221705
}
21703-
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
21706+
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
2170421707
/* reached end of iteration */
2170521708
if (UNEXPECTED(EG(exception) != NULL)) {
2170621709
UNDEF_RESULT();
@@ -21709,7 +21712,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
2170921712
goto fe_fetch_r_exit;
2171021713
}
2171121714
}
21712-
value = iter->funcs->get_current_data(iter);
21715+
value = funcs->get_current_data(iter);
2171321716
if (UNEXPECTED(EG(exception) != NULL)) {
2171421717
UNDEF_RESULT();
2171521718
HANDLE_EXCEPTION();
@@ -21719,8 +21722,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
2171921722
goto fe_fetch_r_exit;
2172021723
}
2172121724
if (RETURN_VALUE_USED(opline)) {
21722-
if (iter->funcs->get_current_key) {
21723-
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
21725+
if (funcs->get_current_key) {
21726+
funcs->get_current_key(iter, EX_VAR(opline->result.var));
2172421727
if (UNEXPECTED(EG(exception) != NULL)) {
2172521728
UNDEF_RESULT();
2172621729
HANDLE_EXCEPTION();
@@ -21845,15 +21848,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
2184521848
}
2184621849
}
2184721850
} else {
21851+
const zend_object_iterator_funcs *funcs = iter->funcs;
2184821852
if (++iter->index > 0) {
2184921853
/* This could cause an endless loop if index becomes zero again.
2185021854
* In case that ever happens we need an additional flag. */
21851-
iter->funcs->move_forward(iter);
21855+
funcs->move_forward(iter);
2185221856
if (UNEXPECTED(EG(exception) != NULL)) {
2185321857
UNDEF_RESULT();
2185421858
HANDLE_EXCEPTION();
2185521859
}
21856-
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
21860+
if (UNEXPECTED(funcs->valid(iter) == FAILURE)) {
2185721861
/* reached end of iteration */
2185821862
if (UNEXPECTED(EG(exception) != NULL)) {
2185921863
UNDEF_RESULT();
@@ -21862,7 +21866,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
2186221866
goto fe_fetch_w_exit;
2186321867
}
2186421868
}
21865-
value = iter->funcs->get_current_data(iter);
21869+
value = funcs->get_current_data(iter);
2186621870
if (UNEXPECTED(EG(exception) != NULL)) {
2186721871
UNDEF_RESULT();
2186821872
HANDLE_EXCEPTION();
@@ -21872,8 +21876,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
2187221876
goto fe_fetch_w_exit;
2187321877
}
2187421878
if (RETURN_VALUE_USED(opline)) {
21875-
if (iter->funcs->get_current_key) {
21876-
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
21879+
if (funcs->get_current_key) {
21880+
funcs->get_current_key(iter, EX_VAR(opline->result.var));
2187721881
if (UNEXPECTED(EG(exception) != NULL)) {
2187821882
UNDEF_RESULT();
2187921883
HANDLE_EXCEPTION();

0 commit comments

Comments
 (0)