Optimize SplFixedArray::fromArray() for packed arrays (#18196)

If the array is packed, then we don't have to loop to get the highest
index.

For this script:
```php
$array = range(1, 100);
for ($i=0;$i<1000000;$i++) {
    SplFixedArray::fromArray($array);
}
```

On an i7-4790:
```
Benchmark 1: ./sapi/cli/php spl.php
  Time (mean ± σ):     376.5 ms ±   2.0 ms    [User: 372.1 ms, System: 2.6 ms]
  Range (min … max):   373.7 ms … 379.5 ms    10 runs

Benchmark 2: ./sapi/cli/php_old spl.php
  Time (mean ± σ):     511.6 ms ±   1.9 ms    [User: 508.0 ms, System: 2.3 ms]
  Range (min … max):   509.2 ms … 515.1 ms    10 runs

Summary
  ./sapi/cli/php spl.php  ran
    1.36 ± 0.01 times faster than ./sapi/cli/php_old spl.php
```

On an i7-1185G7:
```
Benchmark 1: ./sapi/cli/php spl.php
  Time (mean ± σ):     250.4 ms ±   3.5 ms    [User: 246.6 ms, System: 2.6 ms]
  Range (min … max):   247.0 ms … 258.5 ms    11 runs

Benchmark 2: ./sapi/cli/php_old spl.php
  Time (mean ± σ):     328.4 ms ±   1.0 ms    [User: 324.4 ms, System: 3.8 ms]
  Range (min … max):   327.5 ms … 331.0 ms    10 runs

Summary
  ./sapi/cli/php spl.php  ran
    1.31 ± 0.02 times faster than ./sapi/cli/php_old spl.php
```

Bonus: this also decreases the code size of the function.
This commit is contained in:
Niels Dossche 2025-03-30 20:07:49 +02:00 committed by GitHub
parent f056636086
commit e034b69fa6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -733,22 +733,29 @@ PHP_METHOD(SplFixedArray, fromArray)
zend_ulong num_index, max_index = 0; zend_ulong num_index, max_index = 0;
zend_long tmp; zend_long tmp;
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) { if (HT_IS_PACKED(Z_ARRVAL_P(data))) {
if (str_index != NULL || (zend_long)num_index < 0) { /* If there are no holes, then nNumUsed is the number of elements.
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys"); * If there are holes, then nNumUsed is the index of the last element. */
tmp = Z_ARRVAL_P(data)->nNumUsed;
} else {
ZEND_HASH_MAP_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) {
if (str_index != NULL || (zend_long)num_index < 0) {
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys");
RETURN_THROWS();
}
if (num_index > max_index) {
max_index = num_index;
}
} ZEND_HASH_FOREACH_END();
tmp = max_index + 1;
if (tmp <= 0) {
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
RETURN_THROWS(); RETURN_THROWS();
} }
if (num_index > max_index) {
max_index = num_index;
}
} ZEND_HASH_FOREACH_END();
tmp = max_index + 1;
if (tmp <= 0) {
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
RETURN_THROWS();
} }
spl_fixedarray_init(&array, tmp); spl_fixedarray_init(&array, tmp);
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(data), num_index, element) { ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(data), num_index, element) {