Skip to content

[ext/standard] Specialize min()/max() for numeric arrays#22127

Open
mehmetcansahin wants to merge 3 commits into
php:masterfrom
mehmetcansahin:minmax-long-array-fast-path
Open

[ext/standard] Specialize min()/max() for numeric arrays#22127
mehmetcansahin wants to merge 3 commits into
php:masterfrom
mehmetcansahin:minmax-long-array-fast-path

Conversation

@mehmetcansahin
Copy link
Copy Markdown
Contributor

@mehmetcansahin mehmetcansahin commented May 22, 2026

Summary

This specializes the array-form min() / max() path for numeric arrays.

The fast path compares homogeneous IS_LONG and IS_DOUBLE values directly. If the array is empty or the first value is not numeric, execution falls back to the existing zend_hash_minmax() path. If a mixed value is encountered after a numeric prefix, comparison switches back to the existing php_data_compare() semantics for the rest of the scan. NaN-sensitive double cases are handed to the generic comparison path to preserve existing ordering behavior.

For arrays that remain all-long or all-double, the result is returned directly with ZVAL_LONG() or ZVAL_DOUBLE(), avoiding generic comparison dispatch and zval copy/deref overhead on the hot path.

An UPGRADING entry was added under Performance Improvements.

Benchmark

Local CLI build:

  • Debug Build => no
  • --disable-all --enable-cli
  • opcache/JIT disabled
  • separate baseline, long-only, and current CLI binaries
  • n=100000
  • each sample runs 700 min()+max() iterations
  • 5 samples per case
  • table reports median seconds
Case origin/master long-only current long+double Result vs master
packed long 0.612s 0.174s 0.171s 3.58x faster
sparse long 0.492s 0.163s 0.162s 3.04x faster
packed double 0.796s 0.747s 0.164s 4.86x faster
sparse double 0.757s 0.699s 0.158s 4.80x faster
mixed long, late fallback 0.573s 0.172s 0.168s 3.41x faster
mixed double, late fallback 0.781s 0.743s 0.165s 4.74x faster
double with late NaN 0.818s 0.743s 0.163s 5.01x faster

Fallback / non-numeric cases:

Case origin/master current long+double
first string, then longs 1.540s 1.430s
strings only 2.818s 2.901s

Across repeated samples, integer-only arrays stay roughly 3x faster and float-only arrays are roughly 4.8x-5x faster. Generic fallback cases stay within noise to small overhead.

Testing

  • git diff --check
  • git diff --cached --check
  • TEST_PHP_EXECUTABLE=/Users/mehmetcan/work/php/php-src/sapi/cli/php ./sapi/cli/php -n run-tests.php -n -q ext/standard/tests/array/min_max_array_fast_path.phpt

Result:

  • 1 test passed
  • 0 failed
  • 0 warned

@mehmetcansahin mehmetcansahin marked this pull request as ready for review May 22, 2026 14:03
@mehmetcansahin mehmetcansahin requested a review from bukka as a code owner May 22, 2026 14:03
@LamentXU123
Copy link
Copy Markdown
Contributor

You may need to add an entry to the UPGRADING file, Performance Improvements section.
https://github.com/php/php-src/blob/master/UPGRADING#L382

@mehmetcansahin mehmetcansahin changed the title [ext/standard] Specialize min()/max() for long arrays [ext/standard] Specialize min()/max() for numeric arrays May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants