From e034b69fa6f9f3ac9c409f108ae91b4f682660d7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 30 Mar 2025 20:07:49 +0200 Subject: [PATCH] Optimize SplFixedArray::fromArray() for packed arrays (#18196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- ext/spl/spl_fixedarray.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index b1879496990..99e6983b0ff 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -733,22 +733,29 @@ PHP_METHOD(SplFixedArray, fromArray) zend_ulong num_index, max_index = 0; zend_long tmp; - ZEND_HASH_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"); + if (HT_IS_PACKED(Z_ARRVAL_P(data))) { + /* If there are no holes, then nNumUsed is the number of elements. + * 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(); } - - 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); ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(data), num_index, element) {