From 7b6b233ce8ea0c19bdac305781cb35fad1a31aff Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 3 Nov 2025 22:35:26 +0100 Subject: [PATCH] Optimize array_fill_keys() (#20347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the refcount update outside of the loop. For the following benchmark: ```php $r = range(0, 1000); $v = new stdClass(); for ($i = 0; $i < 100000; $i++) { array_fill_keys($r, $v); } ``` On an i7-4790: ``` Benchmark 1: ./sapi/cli/php_old ../x.php Time (mean ± σ): 507.5 ms ± 4.8 ms [User: 505.1 ms, System: 1.2 ms] Range (min … max): 501.2 ms … 518.4 ms 10 runs Benchmark 2: ./sapi/cli/php ../x.php Time (mean ± σ): 479.8 ms ± 3.1 ms [User: 476.8 ms, System: 1.8 ms] Range (min … max): 475.0 ms … 486.7 ms 10 runs Summary ./sapi/cli/php ../x.php ran 1.06 ± 0.01 times faster than ./sapi/cli/php_old ../x.php ``` On an i7-1185G7: ``` Benchmark 1: ./sapi/cli/php x.php Time (mean ± σ): 343.9 ms ± 3.1 ms [User: 341.1 ms, System: 2.3 ms] Range (min … max): 337.9 ms … 347.8 ms 10 runs Benchmark 2: ./sapi/cli/php_old x.php Time (mean ± σ): 357.8 ms ± 2.3 ms [User: 355.7 ms, System: 1.6 ms] Range (min … max): 355.0 ms … 362.6 ms 10 runs Summary ./sapi/cli/php x.php ran 1.04 ± 0.01 times faster than ./sapi/cli/php_old x.php ``` --- UPGRADING | 3 +++ Zend/zend_hash.h | 11 +++++++++++ ext/standard/array.c | 9 ++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/UPGRADING b/UPGRADING index 7a3aff95da8..858e1cffa95 100644 --- a/UPGRADING +++ b/UPGRADING @@ -122,3 +122,6 @@ PHP 8.6 UPGRADE NOTES - JSON: . Improve performance of encoding arrays and objects. + +- Standard: + . Improved performance of array_fill_keys(). diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 57020bbcad0..3a501b7f37d 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -473,6 +473,17 @@ static zend_always_inline bool zend_hash_str_exists_ind(const HashTable *ht, con Z_TYPE_P(Z_INDIRECT_P(zv)) != IS_UNDEF); } +static zend_always_inline zval *zend_symtable_add(HashTable *ht, zend_string *key, zval *pData) +{ + zend_ulong idx; + + if (ZEND_HANDLE_NUMERIC(key, idx)) { + return zend_hash_index_add(ht, idx, pData); + } else { + return zend_hash_add(ht, key, pData); + } +} + static zend_always_inline zval *zend_symtable_add_new(HashTable *ht, zend_string *key, zval *pData) { zend_ulong idx; diff --git a/ext/standard/array.c b/ext/standard/array.c index 4097d718990..32d28fe7022 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2830,16 +2830,19 @@ PHP_FUNCTION(array_fill_keys) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) { ZVAL_DEREF(entry); - Z_TRY_ADDREF_P(val); if (Z_TYPE_P(entry) == IS_LONG) { - zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val); + zend_hash_index_add(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val); } else { zend_string *tmp_key; zend_string *key = zval_get_tmp_string(entry, &tmp_key); - zend_symtable_update(Z_ARRVAL_P(return_value), key, val); + zend_symtable_add(Z_ARRVAL_P(return_value), key, val); zend_tmp_string_release(tmp_key); } } ZEND_HASH_FOREACH_END(); + + if (Z_REFCOUNTED_P(val)) { + GC_ADDREF_EX(Z_COUNTED_P(val), zend_hash_num_elements(Z_ARRVAL_P(return_value))); + } } /* }}} */