Skip to content

Commit df486fd

Browse files
committed
Optimize SplFixedArray dimension performance
This patch optimizes reading and writing from SplFixedArray with the dimension operators. It accomplishes this due to the following optimizations: * Fast-path for long keys (inlined). * Optimization hints (UNEXPECTED + assertion) * Using an unsigned index so we can do a single length comparison For the following script: ```php $test = new SplFixedArray(4); for ($i = 0 ; $i< 5000000; $i++) $test[1] += $i; ``` On an i7-4790: ``` Benchmark 1: ./sapi/cli/php x.php Time (mean ± σ): 95.4 ms ± 1.6 ms [User: 91.5 ms, System: 3.2 ms] Range (min … max): 93.7 ms … 100.8 ms 31 runs Benchmark 2: ./sapi/cli/php_old x.php Time (mean ± σ): 119.1 ms ± 1.3 ms [User: 114.7 ms, System: 3.6 ms] Range (min … max): 117.6 ms … 123.1 ms 24 runs Summary ./sapi/cli/php x.php ran 1.25 ± 0.03 times faster than ./sapi/cli/php_old x.php ``` On an i7-1185G7: ``` Benchmark 1: ./sapi/cli/php x.php Time (mean ± σ): 67.9 ms ± 1.1 ms [User: 64.8 ms, System: 3.2 ms] Range (min … max): 66.6 ms … 72.8 ms 43 runs Benchmark 2: ./sapi/cli/php_old x.php Time (mean ± σ): 84.8 ms ± 1.1 ms [User: 81.0 ms, System: 3.9 ms] Range (min … max): 82.6 ms … 88.0 ms 34 runs Summary ./sapi/cli/php x.php ran 1.25 ± 0.03 times faster than ./sapi/cli/php_old x.php ```
1 parent 5dd9b0d commit df486fd

File tree

1 file changed

+25
-24
lines changed

1 file changed

+25
-24
lines changed

ext/spl/spl_fixedarray.c

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,14 @@ static zend_object *spl_fixedarray_object_clone(zend_object *old_object)
324324
return new_object;
325325
}
326326

327-
static zend_long spl_offset_convert_to_long(zval *offset) /* {{{ */
327+
static zend_never_inline zend_ulong spl_offset_convert_to_ulong_slow(zval *offset) /* {{{ */
328328
{
329329
try_again:
330330
switch (Z_TYPE_P(offset)) {
331331
case IS_STRING: {
332332
zend_ulong index;
333333
if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), index)) {
334-
return (zend_long) index;
334+
return index;
335335
}
336336
break;
337337
}
@@ -356,23 +356,32 @@ static zend_long spl_offset_convert_to_long(zval *offset) /* {{{ */
356356
return 0;
357357
}
358358

359-
static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
359+
static zend_always_inline zend_ulong spl_offset_convert_to_ulong(zval *offset)
360360
{
361-
zend_long index;
361+
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
362+
/* Allow skipping exception check at call-site. */
363+
ZEND_ASSERT(!EG(exception));
364+
return Z_LVAL_P(offset);
365+
} else {
366+
return spl_offset_convert_to_ulong_slow(offset);
367+
}
368+
}
362369

370+
static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
371+
{
363372
/* we have to return NULL on error here to avoid memleak because of
364373
* ZE duplicating uninitialized_zval_ptr */
365374
if (!offset) {
366375
zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
367376
return NULL;
368377
}
369378

370-
index = spl_offset_convert_to_long(offset);
371-
if (EG(exception)) {
379+
zend_ulong index = spl_offset_convert_to_ulong(offset);
380+
if (UNEXPECTED(EG(exception))) {
372381
return NULL;
373382
}
374383

375-
if (index < 0 || index >= intern->array.size) {
384+
if (UNEXPECTED(index >= intern->array.size)) {
376385
zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
377386
return NULL;
378387
} else {
@@ -407,22 +416,19 @@ static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *off
407416

408417
static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *intern, zval *offset, zval *value)
409418
{
410-
zend_long index;
411-
412419
if (!offset) {
413420
/* '$array[] = value' syntax is not supported */
414421
zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
415422
return;
416423
}
417424

418-
index = spl_offset_convert_to_long(offset);
419-
if (EG(exception)) {
425+
zend_ulong index = spl_offset_convert_to_ulong(offset);
426+
if (UNEXPECTED(EG(exception))) {
420427
return;
421428
}
422429

423-
if (index < 0 || index >= intern->array.size) {
430+
if (UNEXPECTED(index >= intern->array.size)) {
424431
zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
425-
return;
426432
} else {
427433
/* Fix #81429 */
428434
zval *ptr = &(intern->array.elements[index]);
@@ -452,16 +458,13 @@ static void spl_fixedarray_object_write_dimension(zend_object *object, zval *off
452458

453459
static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *intern, zval *offset)
454460
{
455-
zend_long index;
456-
457-
index = spl_offset_convert_to_long(offset);
458-
if (EG(exception)) {
461+
zend_ulong index = spl_offset_convert_to_ulong(offset);
462+
if (UNEXPECTED(EG(exception))) {
459463
return;
460464
}
461465

462-
if (index < 0 || index >= intern->array.size) {
466+
if (UNEXPECTED(index >= intern->array.size)) {
463467
zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
464-
return;
465468
} else {
466469
zval garbage;
467470
ZVAL_COPY_VALUE(&garbage, &intern->array.elements[index]);
@@ -483,14 +486,12 @@ static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *off
483486

484487
static bool spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object *intern, zval *offset, bool check_empty)
485488
{
486-
zend_long index;
487-
488-
index = spl_offset_convert_to_long(offset);
489-
if (EG(exception)) {
489+
zend_ulong index = spl_offset_convert_to_ulong(offset);
490+
if (UNEXPECTED(EG(exception))) {
490491
return false;
491492
}
492493

493-
if (index < 0 || index >= intern->array.size) {
494+
if (index >= intern->array.size) {
494495
return false;
495496
}
496497

0 commit comments

Comments
 (0)