Skip to content

Optimize _quickSort with median-of-three pivot selection#6468

Open
Sanenelisiwe1975 wants to merge 1 commit intoOpenZeppelin:masterfrom
Sanenelisiwe1975:fix/quicksort-median-pivot
Open

Optimize _quickSort with median-of-three pivot selection#6468
Sanenelisiwe1975 wants to merge 1 commit intoOpenZeppelin:masterfrom
Sanenelisiwe1975:fix/quicksort-median-pivot

Conversation

@Sanenelisiwe1975
Copy link
Copy Markdown

Using the first element as pivot degrades to O(n²) on already-sorted inputs, which is the most common real-world case. Switch to median-of- three selection (first, middle, last) to guarantee a balanced split on sorted inputs and avoid worst-case behavior.

For arrays of exactly 2 elements the middle and last pointers coincide, so the full three-way selection is skipped and a single comparison sorts them directly.

Fixes #?

Using the first element as pivot degrades to O(n²) on already-sorted inputs, which is the most common real-world case. Switch to median-of-three selection (first, middle, last) to guarantee a balanced split on sorted inputs and avoid worst-case behavior.

For arrays of exactly 2 elements the middle and last pointers coincide, so the full three-way selection is skipped and a single comparison sorts them directly.

Fixes #6289

PR Checklist

  • [ x] Tests
  • Documentation
  • [ x] Changeset entry (run npx changeset add)

@Sanenelisiwe1975 Sanenelisiwe1975 requested a review from a team as a code owner April 7, 2026 11:07
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 7, 2026

🦋 Changeset detected

Latest commit: d8be4c4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

Walkthrough

The pull request optimizes the _quickSort function in the Arrays utilities by implementing a median-of-three pivot selection strategy. Instead of always selecting the first element as the pivot, the updated implementation computes the middle position and performs comparisons among the first, middle, and last elements to select the median value, which is then used as the pivot. This change is applied to both the Solidity contract implementation in contracts/utils/Arrays.sol and the JavaScript template that generates it in scripts/generate/templates/Arrays.js. A corresponding Changeset entry was added to document this patch-level change.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR addresses issue #6289 by implementing median-of-three pivot selection to improve worst-case behavior on sorted inputs, though it does not implement the tail-recursion loop optimization suggested as the primary solution. Consider whether median-of-three pivot alone sufficiently addresses the stack depth concerns mentioned in #6289, or if tail-recursion loop optimization remains necessary for complete resolution.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: optimizing _quickSort with median-of-three pivot selection.
Description check ✅ Passed The description clearly explains the motivation and implementation of the median-of-three pivot selection optimization, directly related to the changeset.
Out of Scope Changes check ✅ Passed All changes are confined to implementing median-of-three pivot selection in the _quickSort function and its generation template, directly aligned with the PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/generate/templates/Arrays.js (1)

67-94: ⚠️ Potential issue | 🟠 Major

Duplicate-heavy arrays still keep the linear-recursion worst case.

With the current 2-way partition, values equal to the pivot never move left, so an all-equal sorted array still leaves pos == begin and recurses on n - 1 elements per step. That means the stack-depth failure from #6289 is still reachable on a valid sorted input, and the “avoids worst-case O(n²)” wording here is too strong. Please at least recurse on the smaller partition and iterate on the larger one, and consider a 3-way partition if you want duplicate-heavy inputs to avoid the quadratic path as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate/templates/Arrays.js` around lines 67 - 94, The current 2-way
partition in _quickSort leaves pos == begin for arrays with many pivots equal to
pivot, causing linear recursion depth; change _quickSort so after partitioning
(using pivot, pos, _swap, comp) you always recurse on the smaller side and loop
(iterate) to sort the larger side to guarantee O(log n) stack depth (i.e.,
replace the two recursive calls _quickSort(begin, pos, comp) and _quickSort(pos
+ 0x20, end, comp) with logic that determines which side is smaller and recurses
on that one while updating begin/end for the loop to sort the larger), and
optionally consider implementing a 3-way partition (Dutch National Flag) around
pivot to handle duplicate-heavy inputs more efficiently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@scripts/generate/templates/Arrays.js`:
- Around line 67-94: The current 2-way partition in _quickSort leaves pos ==
begin for arrays with many pivots equal to pivot, causing linear recursion
depth; change _quickSort so after partitioning (using pivot, pos, _swap, comp)
you always recurse on the smaller side and loop (iterate) to sort the larger
side to guarantee O(log n) stack depth (i.e., replace the two recursive calls
_quickSort(begin, pos, comp) and _quickSort(pos + 0x20, end, comp) with logic
that determines which side is smaller and recurses on that one while updating
begin/end for the loop to sort the larger), and optionally consider implementing
a 3-way partition (Dutch National Flag) around pivot to handle duplicate-heavy
inputs more efficiently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 93203aeb-1cb9-4765-8107-22a915117731

📥 Commits

Reviewing files that changed from the base of the PR and between 9cfdccd and 5d7ad28.

📒 Files selected for processing (3)
  • .changeset/quicksort-median-pivot.md
  • contracts/utils/Arrays.sol
  • scripts/generate/templates/Arrays.js

@Sanenelisiwe1975 Sanenelisiwe1975 force-pushed the fix/quicksort-median-pivot branch from 5d7ad28 to ddcb6d2 Compare April 7, 2026 11:44
Using the first element as pivot degrades to O(n²) on already-sorted
inputs, which is the most common real-world case. Switch to median-of-
three selection (first, middle, last) to guarantee a balanced split on
sorted inputs and avoid worst-case behavior.

For arrays of exactly 2 elements the middle and last pointers coincide,
so the full three-way selection is skipped and a single comparison sorts
them directly.
@Sanenelisiwe1975 Sanenelisiwe1975 force-pushed the fix/quicksort-median-pivot branch from ddcb6d2 to d8be4c4 Compare April 7, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Function _quickSort() can be optimized

2 participants