Skip to content

Zend: add_function_array() fast paths for an empty operand. - #24247

Draft
devnexen wants to merge 1 commit into
php:masterfrom
devnexen:add_function_array_optim
Draft

devnexen wants to merge 1 commit into
php:masterfrom
devnexen:add_function_array_optim

Conversation

@devnexen

Copy link
Copy Markdown
Member

Skip the duplicate and merge when either operand is empty, unless sharing would change the internal pointer or next free index.

@devnexen

Copy link
Copy Markdown
Member Author

Some numbers, release build without opcache, best of 7 runs, in ms.

8 elements, 1M iterations:

hash before hash after list before list after
$a + [] 36.3 6.2 26.8 6.2
[] + $a 50.2 7.2 46.5 6.6
$x = []; $x += $a 51.5 10.2 47.4 9.7
$a + $b 86.0 89.1 44.3 44.7

100 elements, 100k iterations:

hash before hash after list before list after
$a + [] 24.8 0.6 9.9 0.6
[] + $a 66.9 0.8 42.6 0.7
$x = []; $x += $a 63.6 1.0 42.6 1.0
$a + $b 83.4 84.7 26.1 26.4

$a + $b is there as a control, the difference is noise on my machine.

@devnexen
devnexen marked this pull request as ready for review October 10, 2026 20:20
Comment thread Zend/zend_operators.c
Comment thread Zend/zend_operators.c Outdated
Comment on lines +1219 to +1220
src->nInternalPointer == 0 &&
src->nNextFreeElement == next_free) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed code uses a mix of tabs and spaces

Skip the duplicate and merge when either operand is empty, unless
sharing would change the internal pointer or next free index.
@devnexen
devnexen force-pushed the add_function_array_optim branch from 949605d to 7a6c78c Compare October 11, 2026 16:32

@ndossche ndossche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks so complicated and I'm still not convinced it's correct; I can't follow the soundness of the second case.

The first case definitely isn't correct. zend_array_dup() deals with references. So this behaviour changes:

$x = 1;
$b = ['a' => &$x];
$r = [] + $b;
unset($x);
$b['a'] = 5;
echo $r['a'];

Comment thread Zend/zend_operators.c
if (zend_hash_num_elements(Z_ARRVAL_P(op2)) == 0 &&
Z_ARRVAL_P(op1)->nInternalPointer < Z_ARRVAL_P(op1)->nNumUsed) {
/* $a += [] or $a + [], unless the internal pointer is past the
* end as zend_array_dup() would reset it */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments make it sound like the internal pointer is the only thing that changes, which is misleading

@devnexen

Copy link
Copy Markdown
Member Author

This looks so complicated and I'm still not convinced it's correct; I can't follow the soundness of the second case.

The first case definitely isn't correct. zend_array_dup() deals with references. So this behaviour changes:

$x = 1;
$b = ['a' => &$x];
$r = [] + $b;
unset($x);
$b['a'] = 5;
echo $r['a'];

ah good point, it is ... I ll see about these.

@devnexen
devnexen marked this pull request as draft October 11, 2026 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants