[libcxx-commits] [libcxx] [libc++][pstl] Implement pstl std::min_element (PR #173970)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Dec 30 08:19:35 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Marcell Leleszi (mleleszi)

<details>
<summary>Changes</summary>

This patch implements the PSTL variant of `std::min_element` for all execution policies.

The `unseq` version uses a two pass SIMD approach, the first pass to find min value, and the second pass to find the index of the first min value.

The `par` version is built on transform_reduce provided by the backend. The `par_unseq` is the combination of the two, using the SIMD variant within bricks.

It also adds specializations of `__desugars_to` for `__totally_ordered_greater_tag`, so if `std::greater` or `std::less` is used with integral types we can use `==` for equality checks instead of a double comparison to save a comparison.

WIP



---

Patch is 29.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/173970.diff


16 Files Affected:

- (modified) libcxx/docs/Status/PSTLPaper.csv (+1-1) 
- (modified) libcxx/include/CMakeLists.txt (+1) 
- (modified) libcxx/include/__algorithm/pstl.h (+23) 
- (modified) libcxx/include/__functional/operations.h (+6) 
- (modified) libcxx/include/__functional/ranges_operations.h (+3) 
- (modified) libcxx/include/__pstl/backend_fwd.h (+6) 
- (modified) libcxx/include/__pstl/backends/libdispatch.h (+5) 
- (modified) libcxx/include/__pstl/backends/serial.h (+10) 
- (modified) libcxx/include/__pstl/backends/std_thread.h (+5) 
- (added) libcxx/include/__pstl/cpu_algos/min_element.h (+216) 
- (modified) libcxx/include/__type_traits/desugars_to.h (+8) 
- (modified) libcxx/include/module.modulemap.in (+3) 
- (added) libcxx/test/benchmarks/algorithms/pstl.min_element.bench.cpp (+116) 
- (modified) libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp (+5) 
- (added) libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.nodiscard.verify.cpp (+35) 
- (added) libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp (+109) 


``````````diff
diff --git a/libcxx/docs/Status/PSTLPaper.csv b/libcxx/docs/Status/PSTLPaper.csv
index 3fb5adfe3ec46..569eb81541468 100644
--- a/libcxx/docs/Status/PSTLPaper.csv
+++ b/libcxx/docs/Status/PSTLPaper.csv
@@ -33,7 +33,7 @@ Section,Description,Assignee,Complete
 | `[alg.lex.comparison] <https://wg21.link/alg.lex.comparison>`_,std::lexicographical_compare,Nikolas Klauser,|Not Started|
 | `[alg.min.max] <https://wg21.link/alg.min.max>`_,std::max_element,Nikolas Klauser,|Not Started|
 | `[alg.merge] <https://wg21.link/alg.merge>`_,std::merge,Nikolas Klauser,|Complete|
-| `[alg.min.max] <https://wg21.link/alg.min.max>`_,std::min_element,Nikolas Klauser,|Not Started|
+| `[alg.min.max] <https://wg21.link/alg.min.max>`_,std::min_element,Marcell Leleszi,|Complete|
 | `[alg.min.max] <https://wg21.link/alg.min.max>`_,std::minmax_element,Nikolas Klauser,|Not Started|
 | `[mismatch] <https://wg21.link/mismatch>`_,std::mismatch,Nikolas Klauser,|Not Started|
 | `[alg.move] <https://wg21.link/alg.move>`_,std::move,Nikolas Klauser,|Complete|
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 9df40eab678a2..348b3a9a6fd0b 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -659,6 +659,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/min_element.h
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index eea07e2b96b64..804af4ef157b9 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -654,6 +654,29 @@ _LIBCPP_HIDE_FROM_ABI _ForwardOutIterator transform(
       std::move(__op));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+min_element(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "min_element requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__min_element, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+min_element(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "min_element requires ForwardIterators");
+  return std::min_element(std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), less{});
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__functional/operations.h b/libcxx/include/__functional/operations.h
index 7f315ca851c08..882b0b0a8c737 100644
--- a/libcxx/include/__functional/operations.h
+++ b/libcxx/include/__functional/operations.h
@@ -462,6 +462,9 @@ _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(greater);
 template <class _Tp>
 inline const bool __desugars_to_v<__greater_tag, greater<_Tp>, _Tp, _Tp> = true;
 
+template <class _Tp>
+inline const bool __desugars_to_v<__totally_ordered_greater_tag, greater<_Tp>, _Tp, _Tp> = is_integral<_Tp>::value;
+
 #if _LIBCPP_STD_VER >= 14
 template <>
 struct greater<void> {
@@ -477,6 +480,9 @@ struct greater<void> {
 template <class _Tp, class _Up>
 inline const bool __desugars_to_v<__greater_tag, greater<>, _Tp, _Up> = true;
 
+template <class _Tp>
+inline const bool __desugars_to_v<__totally_ordered_greater_tag, greater<>, _Tp, _Tp> = is_integral<_Tp>::value;
+
 template <class _Tp>
 struct __make_transparent<greater<_Tp>> {
   using type _LIBCPP_NODEBUG = greater<>;
diff --git a/libcxx/include/__functional/ranges_operations.h b/libcxx/include/__functional/ranges_operations.h
index dc9da061af264..8368afa76591b 100644
--- a/libcxx/include/__functional/ranges_operations.h
+++ b/libcxx/include/__functional/ranges_operations.h
@@ -109,6 +109,9 @@ inline const bool __desugars_to_v<__less_tag, ranges::less, _Tp, _Up> = true;
 template <class _Tp, class _Up>
 inline const bool __desugars_to_v<__greater_tag, ranges::greater, _Tp, _Up> = true;
 
+template <class _Tp, class _Up>
+inline const bool __desugars_to_v<__totally_ordered_greater_tag, ranges::greater, _Tp, _Up> = true;
+
 template <>
 inline const bool __is_generic_transparent_comparator_v<ranges::less> = true;
 
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a7d53b6a1c989..523ea73faff9e 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -297,6 +297,12 @@ struct __reduce;
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last,
 //                       _Tp __init, _BinaryOperation __op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __min_element;
+// template <class _Policy, class _ForwardIterator, class _Comp>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 88d4231d29a0a..b73f0e6353033 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/min_element.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -392,6 +393,10 @@ template <class _ExecutionPolicy>
 struct __fill<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_fill<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __min_element<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_min_element<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..47a59ad02430c 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/min_element.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -175,6 +176,15 @@ struct __transform_reduce_binary<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __min_element<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _Comp>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
+    return std::min_element(std::move(__first), std::move(__last), std::forward<_Comp>(__comp));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..1b1f4f206e30d 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/min_element.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -129,6 +130,10 @@ template <class _ExecutionPolicy>
 struct __fill<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_fill<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __min_element<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_min_element<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/cpu_algos/min_element.h b/libcxx/include/__pstl/cpu_algos/min_element.h
new file mode 100644
index 0000000000000..a3e440314bd74
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/min_element.h
@@ -0,0 +1,216 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___PSTL_CPU_ALGOS_MIN_ELEMENT_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MIN_ELEMENT_H
+
+#include <__algorithm/min_element.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__type_traits/desugars_to.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__utility/move.h>
+#include <__utility/unreachable.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+// Check if the comparator is totally ordered, and if it is,
+// we can use == instead of the double comparison !(comp(a,b) && !comp(b,a)).
+template <class _Compare, class _Tp>
+inline constexpr bool __desugars_to_totally_ordered_v =
+    __desugars_to_v<__totally_ordered_less_tag, _Compare, _Tp, _Tp> ||
+    __desugars_to_v<__totally_ordered_greater_tag, _Compare, _Tp, _Tp>;
+
+template <class _Backend, class _Index, class _DifferenceType, class _Compare>
+_LIBCPP_HIDE_FROM_ABI _Index __simd_min_element(_Index __first, _DifferenceType __n, _Compare __comp) noexcept {
+  if (__n == 0)
+    return __first;
+
+  using _ValueType                   = __iterator_value_type<_Index>;
+  constexpr size_t __lane_size       = __cpu_traits<_Backend>::__lane_size;
+  const _DifferenceType __block_size = __lane_size / sizeof(_ValueType);
+
+  if (__n < 2 * __block_size || __block_size < 2) {
+    _Index __result = __first;
+    for (_DifferenceType __i = 1; __i < __n; ++__i) {
+      if (__comp(__first[__i], *__result)) {
+        __result = __first + __i;
+      }
+    }
+    return __result;
+  }
+
+  // Pass 1: find minimum value
+  alignas(__lane_size) char __lane_buffer[__lane_size];
+  _ValueType* __lane = reinterpret_cast<_ValueType*>(__lane_buffer);
+
+  // initializer
+  _PSTL_PRAGMA_SIMD
+  for (_DifferenceType __i = 0; __i < __block_size; ++__i) {
+    _ValueType __a = __first[__i];
+    _ValueType __b = __first[__block_size + __i];
+    ::new (__lane + __i) _ValueType(__comp(__a, __b) ? __a : __b);
+  }
+
+  // main loop
+  _DifferenceType __i                    = 2 * __block_size;
+  const _DifferenceType __last_iteration = __block_size * (__n / __block_size);
+  for (; __i < __last_iteration; __i += __block_size) {
+    _PSTL_PRAGMA_SIMD
+    for (_DifferenceType __j = 0; __j < __block_size; ++__j) {
+      if (__comp(__first[__i + __j], __lane[__j])) {
+        __lane[__j] = __first[__i + __j];
+      }
+    }
+  }
+
+  // remainder
+  for (_DifferenceType __j = 0; __j < __n - __last_iteration; ++__j) {
+    if (__comp(__first[__last_iteration + __j], __lane[__j])) {
+      __lane[__j] = __first[__last_iteration + __j];
+    }
+  }
+
+  // combiner
+  _ValueType __min_val = __lane[0];
+  for (_DifferenceType __j = 1; __j < __block_size; ++__j) {
+    if (__comp(__lane[__j], __min_val)) {
+      __min_val = __lane[__j];
+    }
+  }
+
+  // destroyer
+  _PSTL_PRAGMA_SIMD
+  for (_DifferenceType __j = 0; __j < __block_size; ++__j) {
+    __lane[__j].~_ValueType();
+  }
+
+  // Pass 2: find first index with minimum value
+  constexpr _DifferenceType __find_block_size                          = __lane_size / sizeof(_DifferenceType);
+  alignas(__lane_size) _DifferenceType __found_lane[__find_block_size] = {0};
+  _DifferenceType __begin                                              = 0;
+
+  while (__n - __begin >= __find_block_size) {
+    _DifferenceType __found = 0;
+    _PSTL_PRAGMA_SIMD_REDUCTION(| : __found)
+    for (_DifferenceType __k = 0; __k < __find_block_size; ++__k) {
+      _DifferenceType __t;
+      if constexpr (__desugars_to_totally_ordered_v<_Compare, _ValueType>) {
+        __t = __first[__begin + __k] == __min_val;
+      } else {
+        __t = !__comp(__first[__begin + __k], __min_val) && !__comp(__min_val, __first[__begin + __k]);
+      }
+      __found_lane[__k] = __t;
+      __found |= __t;
+    }
+    if (__found) {
+      for (_DifferenceType __k = 0; __k < __find_block_size; ++__k) {
+        if (__found_lane[__k]) {
+          return __first + __begin + __k;
+        }
+      }
+    }
+    __begin += __find_block_size;
+  }
+
+  // remainder
+  while (__begin < __n) {
+    bool __is_equal;
+    if constexpr (__desugars_to_totally_ordered_v<_Compare, _ValueType>) {
+      __is_equal = __first[__begin] == __min_val;
+    } else {
+      __is_equal = !__comp(__first[__begin], __min_val) && !__comp(__min_val, __first[__begin]);
+    }
+    if (__is_equal) {
+      return __first + __begin;
+    }
+    ++__begin;
+  }
+
+  __libcpp_unreachable();
+}
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_min_element {
+  template <class _Policy, class _ForwardIterator, class _Compare>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+      if (__first == __last) {
+        return __last;
+      }
+
+      return __cpu_traits<_Backend>::__transform_reduce(
+          std::move(__first),
+          __last,
+          [](_ForwardIterator __iter) { return __iter; },
+          __last,
+          [__comp, __last](_ForwardIterator __lhs_min, _ForwardIterator __rhs_min) {
+            if (__lhs_min == __last)
+              return __rhs_min;
+            if (__rhs_min == __last)
+              return __lhs_min;
+            if (__comp(*__lhs_min, *__rhs_min))
+              return __lhs_min;
+            if (__comp(*__rhs_min, *__lhs_min))
+              return __rhs_min;
+            return __lhs_min < __rhs_min ? __lhs_min : __rhs_min;
+          },
+          [__comp, __last](_ForwardIterator __brick_first, _ForwardIterator __brick_last, _ForwardIterator __acc) {
+            _ForwardIterator __local_min;
+            if constexpr (__is_unsequenced_execution_policy_v<__remove_parallel_policy_t<_RawExecutionPolicy>> &&
+                          is_trivially_copyable_v<__iterator_value_type<_ForwardIterator>>) {
+              __local_min =
+                  __pstl::__simd_min_element<_Backend>(std::move(__brick_first), __brick_last - __brick_first, __comp);
+            } else {
+              __local_min = std::min_element(std::move(__brick_first), __brick_last, __comp);
+            }
+            if (__local_min == __brick_last)
+              return __acc;
+            if (__acc == __last)
+              return __local_min;
+            if (__comp(*__local_min, *__acc))
+              return __local_min;
+            if (__comp(*__acc, *__local_min))
+              return __acc;
+            return __local_min < __acc ? __local_min : __acc;
+          });
+    } else if constexpr (__is_unsequenced_execution_policy_v<_RawExecutionPolicy> &&
+                         __has_random_access_iterator_category_or_concept<_ForwardIterator>::value &&
+                         is_trivially_copyable_v<__iterator_value_type<_ForwardIterator>>) {
+      return __pstl::__simd_min_element<_Backend>(__first, __last - __first, std::move(__comp));
+    } else {
+      return std::min_element(std::move(__first), std::move(__last), std::move(__comp));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MIN_ELEMENT_H
diff --git a/libcxx/include/__type_traits/desugars_to.h b/libcxx/include/__type_traits/desugars_to.h
index 029b3c6336837..fb0a458dc66bb 100644
--- a/libcxx/include/__type_traits/desugars_to.h
+++ b/libcxx/include/__type_traits/desugars_to.h
@@ -40,6 +40,14 @@ struct __greater_tag {};
 // additional semantic requirements on that operation.
 struct __totally_ordered_less_tag {};
 
+// syntactically, the operation is equivalent to calling `a > b`, and these expressions
+// have to be true for any `a` and `b`:
+// - `(a > b) == (b < a)`
+// - `(!(a > b) && !(b > a)) == (a == b)`
+// For example, this is satisfied for std::greater on integral types, but also for ranges::greater on all types due to
+// additional semantic requirements on that operation.
+struct __totally_ordered_greater_tag {};
+
 // This class template is used to determine whether an operation "desugars"
 // (or boils down) to a given canonical operation.
 //
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index ce168f77dfea4..9a119f47cfb09 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2336,6 +2336,9 @@ module std [system] {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module min_element {
+        header "__pstl/cpu_algos/min_element.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/benchmarks/algorithms/pstl.min_element.bench.cpp b/libcxx/test/benchmarks/algorithms/pstl.min_element.bench.cpp
new file mode 100644
index 0000000000000..f2496f0ad799c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/pstl.min_element.bench.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+#include <algorithm>
+#include <cmath>
+#include <execution>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+struct Seq {
+  static constexpr const auto& policy() { return std::execution::seq; }
+};
+struct Unseq {
+  static constexpr const auto& policy() { return std::execution::unseq; }
+};
+struct Par {
+  static constexpr const auto& policy() { return std::execution::par; }
+};
+struct ParUnseq {
+  static constexpr const auto& policy() { return std::execution::par_unseq; }
+};
+
+struct MinFirst {
+  static size_t pos(size_t) { return 0; }
+};
+struct MinMiddle {
+  static size_t pos(size_t size) { return size / 2; }
+};
+struct MinLast {
+  static size_t pos(size_t size) { return size - 1; }
+};
+
+void run_sizes(auto benchmark) { benchmark->Arg(64)->Arg(512)->Arg(1024)->Arg(4096)->Arg(65536)->Arg(262144); }
+
+template <class T, class Policy, class Position>
+void BM_min_element(benchmark::State& state) {
+  std::vector<T> vec(state.range(), 3);
+  vec[Position::pos(vec.size())] = 1;
+
+  for (auto _ : state) {
+    bench...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/173970


More information about the libcxx-commits mailing list