From 0f5c8a65f9b590b1eb4727086412f7c03159889a Mon Sep 17 00:00:00 2001 From: mehmetcansahin Date: Fri, 22 May 2026 12:06:04 +0300 Subject: [PATCH] Specialize min and max for long arrays --- ext/standard/array.c | 77 ++++++++++++++++++- .../array/min_max_array_long_fast_path.phpt | 44 +++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/array/min_max_array_long_fast_path.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 25259c47d61b..c08ea8abd419 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1095,6 +1095,69 @@ static int php_data_compare(const void *f, const void *s) /* {{{ */ } /* }}} */ +static zend_always_inline bool php_array_minmax_long(HashTable *array, zval *return_value, bool max) /* {{{ */ +{ + zval *entry, *result = NULL; + zend_long result_lval; + bool long_mode = true; + + ZEND_HASH_FOREACH_VAL(array, entry) { + zval *value = entry; + zend_long value_lval; + + ZVAL_DEREF(value); + if (!result) { + if (Z_TYPE_P(value) != IS_LONG) { + return false; + } + + result = value; + result_lval = Z_LVAL_P(value); + continue; + } + + if (long_mode && EXPECTED(Z_TYPE_P(value) == IS_LONG)) { + value_lval = Z_LVAL_P(value); + if (max) { + if (result_lval < value_lval) { + result = value; + result_lval = value_lval; + } + } else { + if (result_lval > value_lval) { + result = value; + result_lval = value_lval; + } + } + continue; + } + + long_mode = false; + + if (max) { + if (php_data_compare(result, value) < 0) { + result = value; + } + } else { + if (php_data_compare(result, value) > 0) { + result = value; + } + } + } ZEND_HASH_FOREACH_END(); + + if (!result) { + return false; + } + + if (long_mode) { + ZVAL_LONG(return_value, result_lval); + } else { + ZVAL_COPY_DEREF(return_value, result); + } + return true; +} +/* }}} */ + /* {{{ * proto mixed min(array values) * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]]) @@ -1114,7 +1177,12 @@ PHP_FUNCTION(min) zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); RETURN_THROWS(); } else { - zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0); + HashTable *array = Z_ARRVAL(args[0]); + if (php_array_minmax_long(array, return_value, false)) { + return; + } + + zval *result = zend_hash_minmax(array, php_data_compare, 0); if (result) { RETURN_COPY_DEREF(result); } else { @@ -1242,7 +1310,12 @@ PHP_FUNCTION(max) zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0])); RETURN_THROWS(); } else { - zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1); + HashTable *array = Z_ARRVAL(args[0]); + if (php_array_minmax_long(array, return_value, true)) { + return; + } + + zval *result = zend_hash_minmax(array, php_data_compare, 1); if (result) { RETURN_COPY_DEREF(result); } else { diff --git a/ext/standard/tests/array/min_max_array_long_fast_path.phpt b/ext/standard/tests/array/min_max_array_long_fast_path.phpt new file mode 100644 index 000000000000..26ace9bad0df --- /dev/null +++ b/ext/standard/tests/array/min_max_array_long_fast_path.phpt @@ -0,0 +1,44 @@ +--TEST-- +min() and max() array long fast path preserves comparison behavior +--FILE-- + $values) { + echo "-- $name --\n"; + var_dump(min($values)); + var_dump(max($values)); +} + +?> +--EXPECT-- +-- packed -- +int(-3) +int(7) +-- sparse -- +int(-2) +int(7) +-- refs -- +int(3) +int(9) +-- first non-long -- +int(3) +string(1) "5" +-- fallback after longs -- +int(2) +int(5)