Skip to content

[ext/standard] Specialize min()/max() for long arrays#22124

Closed
mehmetcansahin wants to merge 1 commit into
php:masterfrom
mehmetcansahin:codex/minmax-long-array-fast-path
Closed

[ext/standard] Specialize min()/max() for long arrays#22124
mehmetcansahin wants to merge 1 commit into
php:masterfrom
mehmetcansahin:codex/minmax-long-array-fast-path

Conversation

@mehmetcansahin
Copy link
Copy Markdown
Contributor

Summary

This specializes the array-form min() / max() path for arrays whose first value is IS_LONG.

The fast path compares IS_LONG values directly. If the array is empty or the first value is not IS_LONG, execution falls back to the existing zend_hash_minmax() path. If a non-IS_LONG value is encountered after a long prefix, comparison switches back to the existing php_data_compare() semantics for the rest of the scan.

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

Float fast paths were intentionally not added: local benchmarks showed no meaningful win, and adding extra type dispatch would increase complexity for little benefit.

Benchmark

Local CLI build:

  • Debug Build => no
  • --disable-all --enable-cli
  • opcache/JIT disabled
  • separate baseline and patched CLI binaries
  • n=10000
  • each case processes roughly 20M elements total
  • 6 alternating baseline/current batches
  • each batch reports median of 7 runs
  • table shows the median across batches
Case Baseline Patched Result
min() packed long desc 73.673 ms 25.309 ms 3.02x faster
max() packed long asc 73.826 ms 25.165 ms 2.94x faster
min() hash long 74.676 ms 25.659 ms 3.02x faster
min() mixed long-prefix 74.130 ms 24.826 ms 2.94x faster
min() mixed non-long first 74.254 ms 75.981 ms +2.3% overhead
min() floats 108.886 ms 109.369 ms +0.4% overhead
min() strings 470.430 ms 452.911 ms within noise

Across the repeated batches, long-array and long-prefix mixed cases were roughly 3x faster. First-non-long, float, and string fallback cases stayed within noise to small overhead.

Testing

  • ./sapi/cli/php run-tests.php -q ext/standard/tests/array/min_max_array_long_fast_path.phpt ext/standard/tests/array/min.phpt ext/standard/tests/array/max.phpt ext/standard/tests/array/min_basic.phpt ext/standard/tests/array/max_basic.phpt ext/standard/tests/array/min_basiclong_64bit.phpt ext/standard/tests/array/max_basiclong_64bit.phpt ext/standard/tests/array/min_int_float_optimisation.phpt ext/standard/tests/array/max_int_float_optimisation.phpt
  • ./sapi/cli/php run-tests.php -q ext/standard/tests/array

Full array test result:

  • 858 tests
  • 846 passed
  • 12 skipped
  • 0 failed
  • 0 warned

Differential behavior hash against baseline:

2d53aa26b0e8da297a97d311a9cb71c86f195b53b25295541ccc06f4996b9303

@mehmetcansahin mehmetcansahin deleted the codex/minmax-long-array-fast-path branch May 22, 2026 12:39
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.

1 participant