Skip to content

Commit 078a286

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 078a286

File tree

1 file changed

+28
-24
lines changed

1 file changed

+28
-24
lines changed

ext/spl/spl_fixedarray.c

Lines changed: 28 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(const 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,35 @@ 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+
/* Returned index is an unsigned number such that we don't have to do a negative check.
360+
* Negative numbers will be mapped at indices larger than ZEND_ULONG_MAX,
361+
* which is beyond the maximum length. */
362+
static zend_always_inline zend_ulong spl_offset_convert_to_ulong(const zval *offset)
360363
{
361-
zend_long index;
364+
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
365+
/* Allow skipping exception check at call-site. */
366+
ZEND_ASSERT(!EG(exception));
367+
return Z_LVAL_P(offset);
368+
} else {
369+
return spl_offset_convert_to_ulong_slow(offset);
370+
}
371+
}
362372

373+
static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
374+
{
363375
/* we have to return NULL on error here to avoid memleak because of
364376
* ZE duplicating uninitialized_zval_ptr */
365377
if (!offset) {
366378
zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
367379
return NULL;
368380
}
369381

370-
index = spl_offset_convert_to_long(offset);
371-
if (EG(exception)) {
382+
zend_ulong index = spl_offset_convert_to_ulong(offset);
383+
if (UNEXPECTED(EG(exception))) {
372384
return NULL;
373385
}
374386

375-
if (index < 0 || index >= intern->array.size) {
387+
if (UNEXPECTED(index >= intern->array.size)) {
376388
zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
377389
return NULL;
378390
} else {
@@ -407,22 +419,19 @@ static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *off
407419

408420
static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *intern, zval *offset, zval *value)
409421
{
410-
zend_long index;
411-
412422
if (!offset) {
413423
/* '$array[] = value' syntax is not supported */
414424
zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
415425
return;
416426
}
417427

418-
index = spl_offset_convert_to_long(offset);
419-
if (EG(exception)) {
428+
zend_ulong index = spl_offset_convert_to_ulong(offset);
429+
if (UNEXPECTED(EG(exception))) {
420430
return;
421431
}
422432

423-
if (index < 0 || index >= intern->array.size) {
433+
if (UNEXPECTED(index >= intern->array.size)) {
424434
zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
425-
return;
426435
} else {
427436
/* Fix #81429 */
428437
zval *ptr = &(intern->array.elements[index]);
@@ -452,16 +461,13 @@ static void spl_fixedarray_object_write_dimension(zend_object *object, zval *off
452461

453462
static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *intern, zval *offset)
454463
{
455-
zend_long index;
456-
457-
index = spl_offset_convert_to_long(offset);
458-
if (EG(exception)) {
464+
zend_ulong index = spl_offset_convert_to_ulong(offset);
465+
if (UNEXPECTED(EG(exception))) {
459466
return;
460467
}
461468

462-
if (index < 0 || index >= intern->array.size) {
469+
if (UNEXPECTED(index >= intern->array.size)) {
463470
zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
464-
return;
465471
} else {
466472
zval garbage;
467473
ZVAL_COPY_VALUE(&garbage, &intern->array.elements[index]);
@@ -483,14 +489,12 @@ static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *off
483489

484490
static bool spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object *intern, zval *offset, bool check_empty)
485491
{
486-
zend_long index;
487-
488-
index = spl_offset_convert_to_long(offset);
489-
if (EG(exception)) {
492+
zend_ulong index = spl_offset_convert_to_ulong(offset);
493+
if (UNEXPECTED(EG(exception))) {
490494
return false;
491495
}
492496

493-
if (index < 0 || index >= intern->array.size) {
497+
if (index >= intern->array.size) {
494498
return false;
495499
}
496500

0 commit comments

Comments
 (0)