diff --git a/UPGRADING b/UPGRADING index 42ee8f4228d4..def406b1d1be 100644 --- a/UPGRADING +++ b/UPGRADING @@ -1001,6 +1001,7 @@ PHP 8.6 UPGRADE NOTES . Reduced temporary allocations when iterating Phar directories. - Standard: + . Improved performance of sorting single-element arrays. . Improved performance of array_fill_keys(). . Improved performance of array_intersect(). . Improved performance of array_map() with multiple arrays passed. diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 99406f9e4192..84751a02e7c0 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -2997,9 +2997,17 @@ static void zend_hash_sort_internal(HashTable *ht, sort_func_t sort, bucket_comp IS_CONSISTENT(ht); - if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { - /* Doesn't require sorting */ - return; + if (ht->nNumOfElements <= 1) { + if (!renumber || ht->nNumOfElements == 0) { + /* Doesn't require sorting */ + return; + } + if (sort == zend_sort && HT_IS_PACKED(ht) && HT_IS_WITHOUT_HOLES(ht)) { + /* The single element already has the expected index. */ + ht->nInternalPointer = 0; + ht->nNextFreeElement = 1; + return; + } } if (HT_IS_PACKED(ht)) { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 1181bee29fae..c7cdbe95866e 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -312,7 +312,12 @@ static zend_always_inline void ZEND_FASTCALL zend_hash_sort(HashTable *ht, bucke * trigger user code. It will ensure the user code cannot free the array during * sorting. */ static zend_always_inline void zend_array_sort(HashTable *ht, bucket_compare_func_t compare_func, bool renumber) { - zend_array_sort_ex(ht, zend_sort, compare_func, renumber); + /* zend_sort() cannot invoke the comparator for at most one element. */ + if (ht->nNumOfElements <= 1) { + zend_hash_sort_ex(ht, zend_sort, compare_func, renumber); + } else { + zend_array_sort_ex(ht, zend_sort, compare_func, renumber); + } } static zend_always_inline uint32_t zend_hash_num_elements(const HashTable *ht) { diff --git a/ext/standard/tests/array/sort/sort_single_element.phpt b/ext/standard/tests/array/sort/sort_single_element.phpt new file mode 100644 index 000000000000..d7d63e98c733 --- /dev/null +++ b/ext/standard/tests/array/sort/sort_single_element.phpt @@ -0,0 +1,57 @@ +--TEST-- +Sorting single-element arrays does not invoke the comparison function +--FILE-- + $b; +}; + +$array = [42]; +// Keep the array packed and without holes, but leave the next free index at 11. +$array[10] = 99; +unset($array[10]); +next($array); +var_dump(usort($array, $compare)); +var_dump($array, key($array)); +$array[] = 43; +var_dump($array); + +$array = ['answer' => 42]; +next($array); +var_dump(usort($array, $compare)); +var_dump($array, key($array)); +$array[] = 43; +var_dump($array); + +var_dump($calls); + +?> +--EXPECT-- +bool(true) +array(1) { + [0]=> + int(42) +} +int(0) +array(2) { + [0]=> + int(42) + [1]=> + int(43) +} +bool(true) +array(1) { + [0]=> + int(42) +} +int(0) +array(2) { + [0]=> + int(42) + [1]=> + int(43) +} +int(0)