[libcxx-commits] [libcxx] Libcxx nth element small ranges (PR #215698)
Adib Pathan via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Aug 12 21:29:25 PDT 2026
https://github.com/adxb-pxthxn updated https://github.com/llvm/llvm-project/pull/215698
>From 938cce62eab4769aa2d575596978037be1c3746c Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 12 Aug 2026 09:03:24 +0930
Subject: [PATCH 1/5] [libc++] Use sorting networks for nth_element on small
ranges
nth_element previously used selection sort for ranges of 4-7 elements.
Dispatch ranges of 4 and 5 elements to the existing __sort4/__sort5
sorting networks instead, and perform that dispatch in the inlinable
wrapper behind __builtin_constant_p so that callers passing a range
whose size is a compile-time constant (e.g. a median-of-5 filter)
compile down to a branchless sorting network with no function call.
Fixes #214238
---
libcxx/docs/ReleaseNotes/24.rst | 4 ++
libcxx/include/__algorithm/nth_element.h | 64 ++++++++++++++++++------
libcxx/include/__algorithm/sort.h | 8 +--
3 files changed, 57 insertions(+), 19 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index e9a36ae94392f..1640bfcd3eb86 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -51,6 +51,10 @@ Implemented Papers
Improvements and New Features
-----------------------------
+- ``std::nth_element`` now uses sorting networks for ranges with at most 5 elements, which makes it significantly
+ faster on small ranges. In particular, when the size of the range is known at compile time (as in a median-of-5
+ filter), the algorithm now compiles down to a branchless sorting network.
+
Deprecations and Removals
-------------------------
diff --git a/libcxx/include/__algorithm/nth_element.h b/libcxx/include/__algorithm/nth_element.h
index da748d7255aba..53aca0b55b6a4 100644
--- a/libcxx/include/__algorithm/nth_element.h
+++ b/libcxx/include/__algorithm/nth_element.h
@@ -28,6 +28,43 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
+// Handles ranges of at most 5 elements by sorting them entirely, which trivially satisfies the
+// nth_element postconditions. Sorting networks are the most efficient way to handle such small
+// ranges, and this function boils down to one of them when the size of the range is known at
+// compile time. Returns false if the range was too large to be handled.
+template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
+__nth_element_small(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
+ using _Ops = _IterOps<_AlgPolicy>;
+ typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
+ switch (__last - __first) {
+ case 0:
+ case 1:
+ return true;
+ case 2:
+ if (__comp(*--__last, *__first))
+ _Ops::iter_swap(__first, __last);
+ return true;
+ case 3:
+ std::__sort3<_AlgPolicy, _Compare>(__first, __first + difference_type(1), --__last, __comp);
+ return true;
+ case 4:
+ std::__sort4<_AlgPolicy, _Compare>(
+ __first, __first + difference_type(1), __first + difference_type(2), --__last, __comp);
+ return true;
+ case 5:
+ std::__sort5<_AlgPolicy, _Compare>(
+ __first,
+ __first + difference_type(1),
+ __first + difference_type(2),
+ __first + difference_type(3),
+ --__last,
+ __comp);
+ return true;
+ }
+ return false;
+}
+
template <class _Compare, class _RandomAccessIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __nth_element_find_guard(
_RandomAccessIterator& __i, _RandomAccessIterator& __j, _RandomAccessIterator __m, _Compare __comp) {
@@ -55,21 +92,9 @@ __nth_element(
while (true) {
if (__nth == __last)
return;
- difference_type __len = __last - __first;
- switch (__len) {
- case 0:
- case 1:
- return;
- case 2:
- if (__comp(*--__last, *__first))
- _Ops::iter_swap(__first, __last);
- return;
- case 3: {
- _RandomAccessIterator __m = __first;
- std::__sort3<_AlgPolicy, _Compare>(__first, ++__m, --__last, __comp);
+ if (std::__nth_element_small<_AlgPolicy, _Compare>(__first, __last, __comp))
return;
- }
- }
+ difference_type __len = __last - __first;
if (__len <= __limit) {
std::__selection_sort<_AlgPolicy, _Compare>(__first, __last, __comp);
return;
@@ -234,7 +259,16 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __nth_element_im
std::__debug_randomize_range<_AlgPolicy>(__first, __last);
- std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
+ // When the size of the range is known at compile time (e.g. in a median-of-5 filter), this
+ // dispatch constant-folds to the appropriate sorting network. Otherwise, the check disappears
+ // entirely and we fall through to the general algorithm, which handles small ranges itself.
+ // Note that __builtin_constant_p is only resolved after inlining, so this catches callers that
+ // pass a constant size even though it is not a constant expression here.
+ bool __small_constant_size =
+ __builtin_constant_p(__last - __first) &&
+ std::__nth_element_small<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __last, __comp);
+ if (!__small_constant_size)
+ std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
std::__debug_randomize_range<_AlgPolicy>(__first, __nth);
if (__nth != __last) {
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..4b450e7dc9194 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -140,7 +140,7 @@ template <class,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
__sort4(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
@@ -157,7 +157,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
__sort4(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
@@ -182,7 +182,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
__sort5(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
@@ -201,7 +201,7 @@ template <class _AlgPolicy,
class _Compare,
class _RandomAccessIterator,
__enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
__sort5(_RandomAccessIterator __x1,
_RandomAccessIterator __x2,
_RandomAccessIterator __x3,
>From 91642f38a620c458c6f4dcf428aaa9045a43456b Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 12 Aug 2026 09:10:50 +0930
Subject: [PATCH 2/5] [libc++] Add exhaustive tests and small-range benchmarks
for nth_element
Add an exhaustive permutation/duplicate test covering the size-0..5
sorting-network paths, and extend the nth_element benchmark with
fixed-size median-of-3/5 windows that match the motivating use case.
---
.../algorithms/sorting/nth_element.bench.cpp | 49 +++++++
.../nth_element.exhaustive.pass.cpp | 123 ++++++++++++++++++
2 files changed, 172 insertions(+)
create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp
diff --git a/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
index 4c3e8115def30..d210e514a35fd 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
@@ -84,6 +84,55 @@ int main(int argc, char** argv) {
register_bm(support::single_element_data<int>, "repeated");
}
+ // Benchmark std::nth_element on many independent fixed-size windows. This models
+ // the median-of-3 / median-of-5 filter use case from #214238, where the window
+ // size is a compile-time constant and the specialized sorting-network path is
+ // expected to constant-fold away the general algorithm.
+ {
+ auto bm = []<std::size_t Window>(std::string name, auto generate_data) {
+ benchmark::RegisterBenchmark(
+ name,
+ [generate_data](auto& st) {
+ std::size_t const num_windows = st.range(0);
+ constexpr std::size_t BatchSize = 32;
+ std::vector<int> flat = generate_data(num_windows * Window);
+ std::array<std::vector<std::array<int, Window>>, BatchSize> batches;
+ for (std::size_t b = 0; b != BatchSize; ++b) {
+ batches[b].resize(num_windows);
+ for (std::size_t i = 0; i != num_windows; ++i) {
+ std::copy_n(flat.begin() + static_cast<std::ptrdiff_t>(i * Window), Window, batches[b][i].begin());
+ }
+ }
+
+ constexpr std::size_t Median = Window / 2;
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t b = 0; b != BatchSize; ++b) {
+ for (std::size_t i = 0; i != num_windows; ++i) {
+ benchmark::DoNotOptimize(batches[b][i]);
+ std::nth_element(batches[b][i].begin(), batches[b][i].begin() + Median, batches[b][i].end());
+ benchmark::DoNotOptimize(batches[b][i]);
+ }
+ }
+
+ st.PauseTiming();
+ for (std::size_t b = 0; b != BatchSize; ++b) {
+ for (std::size_t i = 0; i != num_windows; ++i) {
+ std::copy_n(flat.begin() + static_cast<std::ptrdiff_t>(i * Window), Window, batches[b][i].begin());
+ }
+ }
+ st.ResumeTiming();
+ }
+ })
+ ->Arg(1024)
+ ->Arg(8192);
+ };
+
+ bm.operator()<3>("std::nth_element(median-of-3 windows) (shuffled)", support::shuffled_data<int>);
+ bm.operator()<5>("std::nth_element(median-of-5 windows) (shuffled)", support::shuffled_data<int>);
+ bm.operator()<3>("std::nth_element(median-of-3 windows) (ascending)", support::ascending_sorted_data<int>);
+ bm.operator()<5>("std::nth_element(median-of-5 windows) (ascending)", support::ascending_sorted_data<int>);
+ }
+
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp
new file mode 100644
index 0000000000000..b747a18be8146
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// Exhaustively check std::nth_element on all permutations of small ranges.
+// This specifically covers the size-0..5 sorting-network specializations and the
+// size-6..7 selection-sort fallback.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <functional>
+#include <numeric>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iter, class Compare>
+void check_postcondition(Iter first, Iter nth, Iter last, Compare comp) {
+ if (nth == last)
+ return;
+ for (Iter i = first; i != nth; ++i)
+ assert(!comp(*nth, *i));
+ for (Iter i = nth; i != last; ++i)
+ assert(!comp(*i, *nth));
+}
+
+template <class Iter, class Compare>
+void test_permutations(std::vector<int> values, Compare comp) {
+ std::size_t const size = values.size();
+ std::vector<int> expected = values;
+ std::sort(expected.begin(), expected.end(), comp);
+
+ do {
+ for (std::size_t nth = 0; nth <= size; ++nth) {
+ std::vector<int> v = values;
+ std::nth_element(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size), comp);
+ if (nth != size)
+ assert(v[nth] == expected[nth]);
+ assert(std::is_permutation(v.begin(), v.end(), values.begin()));
+ check_postcondition(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size), comp);
+ }
+ } while (std::next_permutation(values.begin(), values.end()));
+}
+
+template <class Iter>
+void test_distinct() {
+ for (int size = 0; size <= 8; ++size) {
+ std::vector<int> values(static_cast<std::size_t>(size));
+ std::iota(values.begin(), values.end(), 0);
+ test_permutations<Iter>(values, std::less<int>());
+ test_permutations<Iter>(values, std::greater<int>());
+ }
+}
+
+// Exercise equal-element paths with every bit pattern of 0/1 for sizes 1..8.
+template <class Iter>
+void test_duplicates() {
+ for (int size = 1; size <= 8; ++size) {
+ for (int mask = 0; mask < (1 << size); ++mask) {
+ std::vector<int> values(static_cast<std::size_t>(size));
+ for (int i = 0; i < size; ++i)
+ values[static_cast<std::size_t>(i)] = (mask >> i) & 1;
+
+ std::vector<int> expected = values;
+ std::sort(expected.begin(), expected.end());
+ for (int nth = 0; nth < size; ++nth) {
+ std::vector<int> v = values;
+ std::nth_element(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size));
+ assert(v[static_cast<std::size_t>(nth)] == expected[static_cast<std::size_t>(nth)]);
+ check_postcondition(Iter(v.data()), Iter(v.data() + nth), Iter(v.data() + size), std::less<int>());
+ }
+ }
+ }
+}
+
+#if TEST_STD_VER >= 20
+constexpr bool test_constexpr_small_ranges() {
+ // Sizes 3, 4 and 5 hit the specialized sorting-network dispatch.
+ {
+ int a[] = {2, 0, 1};
+ std::nth_element(a, a + 1, a + 3);
+ if (a[1] != 1)
+ return false;
+ }
+ {
+ int a[] = {3, 0, 2, 1};
+ std::nth_element(a, a + 2, a + 4);
+ if (a[2] != 2)
+ return false;
+ }
+ {
+ int a[] = {4, 2, 0, 3, 1};
+ std::nth_element(a, a + 2, a + 5);
+ if (a[2] != 2)
+ return false;
+ }
+ {
+ int a[] = {4, 2, 0, 3, 1};
+ std::nth_element(a, a + 2, a + 5, std::greater<>());
+ if (a[2] != 2)
+ return false;
+ }
+ return true;
+}
+static_assert(test_constexpr_small_ranges());
+#endif
+
+int main(int, char**) {
+ test_distinct<int*>();
+ test_distinct<random_access_iterator<int*> >();
+ test_duplicates<int*>();
+ test_duplicates<random_access_iterator<int*> >();
+
+ return 0;
+}
>From 72cc87f60541b78b5ff20fc5798ce5e53164f349 Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 12 Aug 2026 10:04:16 +0930
Subject: [PATCH 3/5] [libc++] Align nth_element exhaustive test for
clang-format
---
.../alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp
index b747a18be8146..1a471ac296346 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/nth_element.exhaustive.pass.cpp
@@ -34,7 +34,7 @@ void check_postcondition(Iter first, Iter nth, Iter last, Compare comp) {
template <class Iter, class Compare>
void test_permutations(std::vector<int> values, Compare comp) {
- std::size_t const size = values.size();
+ std::size_t const size = values.size();
std::vector<int> expected = values;
std::sort(expected.begin(), expected.end(), comp);
>From 63a2a587735b588fd86cc3165127ef811f99c80f Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Thu, 13 Aug 2026 13:30:25 +0930
Subject: [PATCH 4/5] [libc++] Use a 6-comparison median-of-5 in nth_element
A full sort of 5 elements does more work than nth_element requires.
Select the median with 6 comparisons (Knuth, TAOCP 5.3.3) and partition
around it. Cheap arithmetic types still use a branchless 7-comparator
network.
---
libcxx/include/__algorithm/nth_element.h | 86 +++++++++++++++++++++---
1 file changed, 78 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__algorithm/nth_element.h b/libcxx/include/__algorithm/nth_element.h
index 53aca0b55b6a4..dca936d82d366 100644
--- a/libcxx/include/__algorithm/nth_element.h
+++ b/libcxx/include/__algorithm/nth_element.h
@@ -28,13 +28,79 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
-// Handles ranges of at most 5 elements by sorting them entirely, which trivially satisfies the
-// nth_element postconditions. Sorting networks are the most efficient way to handle such small
-// ranges, and this function boils down to one of them when the size of the range is known at
-// compile time. Returns false if the range was too large to be handled.
+// Selects the median of 5 elements and partitions the other 4 around it, so the nth_element
+// postconditions hold for nth == first + 2. Uses 6 comparisons, which is optimal
+// (Knuth, TAOCP 5.3.3).
+template <class _AlgPolicy,
+ class _Compare,
+ class _RandomAccessIterator,
+ __enable_if_t<!__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
+__nth_element_median5(_RandomAccessIterator __first, _Compare __comp) {
+ using _Ops = _IterOps<_AlgPolicy>;
+
+ _RandomAccessIterator __x1 = __first;
+ _RandomAccessIterator __x2 = __first + 1;
+ _RandomAccessIterator __x3 = __first + 2;
+ _RandomAccessIterator __x4 = __first + 3;
+ _RandomAccessIterator __x5 = __first + 4;
+
+ // Sort pairs (__x1, __x2) and (__x3, __x4).
+ if (__comp(*__x2, *__x1))
+ _Ops::iter_swap(__x1, __x2);
+ if (__comp(*__x4, *__x3))
+ _Ops::iter_swap(__x3, __x4);
+
+ // Order those pairs by their larger element: *__x1 <= *__x2 <= *__x4 and *__x3 <= *__x4.
+ // *__x4 has at least 3 elements <= it, so it cannot be the median.
+ if (__comp(*__x4, *__x2)) {
+ _Ops::iter_swap(__x1, __x3);
+ _Ops::iter_swap(__x2, __x4);
+ }
+
+ // Insert __x5 beside __x3.
+ if (__comp(*__x5, *__x3))
+ _Ops::iter_swap(__x3, __x5);
+
+ // Order pairs (__x1, __x2) and (__x3, __x5) by their larger element:
+ // *__x1 <= *__x2 <= *__x5 and *__x3 <= *__x5.
+ if (__comp(*__x5, *__x2)) {
+ _Ops::iter_swap(__x1, __x3);
+ _Ops::iter_swap(__x2, __x5);
+ }
+
+ // The median is the larger of __x2 and __x3.
+ if (__comp(*__x3, *__x2))
+ _Ops::iter_swap(__x2, __x3);
+}
+
+// Branchless 7-comparator selection network. One extra comparison vs the adaptive algorithm
+// above, but no control flow, which is faster for cheap arithmetic types.
+template <class,
+ class _Compare,
+ class _RandomAccessIterator,
+ __enable_if_t<__use_branchless_sort<_Compare, _RandomAccessIterator>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
+__nth_element_median5(_RandomAccessIterator __first, _Compare __comp) {
+ _RandomAccessIterator __x1 = __first;
+ _RandomAccessIterator __x2 = __first + 1;
+ _RandomAccessIterator __x3 = __first + 2;
+ _RandomAccessIterator __x4 = __first + 3;
+ _RandomAccessIterator __x5 = __first + 4;
+
+ std::__cond_swap<_Compare>(__x1, __x2, __comp);
+ std::__cond_swap<_Compare>(__x3, __x4, __comp);
+ std::__cond_swap<_Compare>(__x1, __x3, __comp);
+ std::__cond_swap<_Compare>(__x2, __x4, __comp);
+ std::__cond_swap<_Compare>(__x2, __x3, __comp);
+ std::__cond_swap<_Compare>(__x2, __x5, __comp);
+ std::__cond_swap<_Compare>(__x3, __x5, __comp);
+}
+
+// Handles ranges of at most 5 elements. Returns false if the range was too large to be handled.
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
-__nth_element_small(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __nth_element_small(
+ _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) {
using _Ops = _IterOps<_AlgPolicy>;
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
switch (__last - __first) {
@@ -53,6 +119,10 @@ __nth_element_small(_RandomAccessIterator __first, _RandomAccessIterator __last,
__first, __first + difference_type(1), __first + difference_type(2), --__last, __comp);
return true;
case 5:
+ if (__nth == __first + difference_type(2)) {
+ std::__nth_element_median5<_AlgPolicy, _Compare>(__first, __comp);
+ return true;
+ }
std::__sort5<_AlgPolicy, _Compare>(
__first,
__first + difference_type(1),
@@ -92,7 +162,7 @@ __nth_element(
while (true) {
if (__nth == __last)
return;
- if (std::__nth_element_small<_AlgPolicy, _Compare>(__first, __last, __comp))
+ if (std::__nth_element_small<_AlgPolicy, _Compare>(__first, __nth, __last, __comp))
return;
difference_type __len = __last - __first;
if (__len <= __limit) {
@@ -266,7 +336,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __nth_element_im
// pass a constant size even though it is not a constant expression here.
bool __small_constant_size =
__builtin_constant_p(__last - __first) &&
- std::__nth_element_small<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __last, __comp);
+ std::__nth_element_small<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
if (!__small_constant_size)
std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
>From 752753e1ab9e952fc6d24088274611679cac872d Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Thu, 13 Aug 2026 13:59:02 +0930
Subject: [PATCH 5/5] [libc++] Drop __builtin_constant_p explanation from
nth_element
---
libcxx/include/__algorithm/nth_element.h | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__algorithm/nth_element.h b/libcxx/include/__algorithm/nth_element.h
index dca936d82d366..9202f67703eba 100644
--- a/libcxx/include/__algorithm/nth_element.h
+++ b/libcxx/include/__algorithm/nth_element.h
@@ -330,10 +330,9 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __nth_element_im
std::__debug_randomize_range<_AlgPolicy>(__first, __last);
// When the size of the range is known at compile time (e.g. in a median-of-5 filter), this
- // dispatch constant-folds to the appropriate sorting network. Otherwise, the check disappears
- // entirely and we fall through to the general algorithm, which handles small ranges itself.
- // Note that __builtin_constant_p is only resolved after inlining, so this catches callers that
- // pass a constant size even though it is not a constant expression here.
+ // dispatch constant-folds to the appropriate small-range algorithm. Otherwise, the check
+ // disappears entirely and we fall through to the general algorithm, which handles small ranges
+ // itself.
bool __small_constant_size =
__builtin_constant_p(__last - __first) &&
std::__nth_element_small<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
More information about the libcxx-commits
mailing list