[libcxx-commits] [libcxx] [libcxx][algorithm] Optimize std::stable_sort via radix sort algorithm (PR #104683)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Aug 21 00:47:16 PDT 2024
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?=,
=?utf-8?b?0JTQvNC40YLRgNC40Lkg0JjQtw=?Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/104683 at github.com>
================
@@ -0,0 +1,366 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___ALGORITHM_RADIX_SORT_H
+#define _LIBCPP___ALGORITHM_RADIX_SORT_H
+
+#include <__algorithm/copy.h>
+#include <__algorithm/for_each.h>
+#include <__bit/countl.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__iterator/distance.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/move_iterator.h>
+#include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
+#include <__numeric/partial_sum.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_assignable.h>
+#include <__type_traits/is_integral.h>
+#include <__type_traits/is_unsigned.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/integer_sequence.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <climits>
+#include <cstdint>
+#include <initializer_list>
+#include <limits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 14
+
+template <class _Iterator,
+ enable_if_t<is_move_assignable<typename iterator_traits<_Iterator>::value_type>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr move_iterator<_Iterator> __move_assign_please(_Iterator __i) {
+ return std::make_move_iterator(std::move(__i));
+}
+
+template <class _Iterator,
+ enable_if_t<!is_move_assignable<typename iterator_traits<_Iterator>::value_type>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr _Iterator __move_assign_please(_Iterator __i) {
+ return __i;
+}
+
+template <class _UnsignedInteger>
+_LIBCPP_HIDE_FROM_ABI constexpr _UnsignedInteger __intlog2(_UnsignedInteger __n) {
+ static_assert(is_unsigned<_UnsignedInteger>::value, "Must be unsigned integral");
+
+ return numeric_limits<_UnsignedInteger>::digits - 1 - std::__countl_zero(__n);
+}
+
+template <class _InputIterator, class _OutputIterator>
+_LIBCPP_HIDE_FROM_ABI pair<_OutputIterator, typename iterator_traits<_InputIterator>::value_type>
+__partial_sum_max(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
+ if (__first == __last)
+ return {__result, 0};
+
+ auto __max = *__first;
+ typename iterator_traits<_InputIterator>::value_type __sum = *__first;
+ *__result = __sum;
+
+ while (++__first != __last) {
+ if (__max < *__first) {
+ __max = *__first;
+ }
+ __sum = std::move(__sum) + *__first;
+ *++__result = __sum;
+ }
+ return {++__result, __max};
+}
+
+template <class _Value, class _Map, class _Radix>
+struct __radix_sort_traits {
+ using image_type = decay_t<invoke_result_t<_Map, _Value> >;
+ static_assert(is_unsigned<image_type>::value, "");
+
+ using radix_type = decay_t<invoke_result_t<_Radix, image_type> >;
+ static_assert(is_integral<radix_type>::value, "");
+
+ constexpr static auto __radix_value_range = numeric_limits<radix_type>::max() + 1;
+ constexpr static auto __radix_size = std::__intlog2<uint64_t>(__radix_value_range);
+ constexpr static auto __radix_count = sizeof(image_type) * CHAR_BIT / __radix_size;
+};
+
+template <class _Value, class _Map>
+struct __counting_sort_traits {
+ using image_type = decay_t<invoke_result_t<_Map, _Value> >;
+ static_assert(is_unsigned<image_type>::value, "");
+
+ constexpr static const auto __value_range = numeric_limits<image_type>::max() + 1;
+ constexpr static auto __radix_size = std::__intlog2<uint64_t>(__value_range);
+};
+
+template <class _Radix>
+_LIBCPP_HIDE_FROM_ABI auto __nth_radix(size_t __radix_number, _Radix __radix) {
+ return [__radix_number, __radix = std::move(__radix)](auto __n) {
+ using value_type = decltype(__n);
+ static_assert(is_unsigned<value_type>::value, "");
+ using traits = __counting_sort_traits<value_type, _Radix>;
+
+ return __radix(static_cast<value_type>(__n >> traits::__radix_size * __radix_number));
+ };
+}
+
+template <class _ForwardIterator, class _Map, class _RandomAccessIterator>
+_LIBCPP_HIDE_FROM_ABI void
+__collect(_ForwardIterator __first, _ForwardIterator __last, _Map __map, _RandomAccessIterator __counters) {
+ using value_type = typename iterator_traits<_ForwardIterator>::value_type;
+ using traits = __counting_sort_traits<value_type, _Map>;
+
+ std::for_each(__first, __last, [&__counters, &__map](const auto& __preimage) { ++__counters[__map(__preimage)]; });
+
+ const auto __counters_end = __counters + traits::__value_range;
+ std::partial_sum(__counters, __counters_end, __counters);
+}
+
+template <class _ForwardIterator, class _RandomAccessIterator1, class _Map, class _RandomAccessIterator2>
+_LIBCPP_HIDE_FROM_ABI void
+__dispose(_ForwardIterator __first,
+ _ForwardIterator __last,
+ _RandomAccessIterator1 __result,
+ _Map __map,
+ _RandomAccessIterator2 __counters) {
+ std::for_each(__first, __last, [&__result, &__counters, &__map](auto&& __preimage) {
+ auto __index = __counters[__map(__preimage)]++;
+ __result[__index] = std::forward<decltype(__preimage)>(__preimage);
+ });
+}
+
+template <class _ForwardIterator,
+ class _Map,
+ class _Radix,
+ class _RandomAccessIterator1,
+ class _RandomAccessIterator2,
+ size_t... _Radices>
+_LIBCPP_HIDE_FROM_ABI bool __collect_impl(
+ _ForwardIterator __first,
+ _ForwardIterator __last,
+ _Map __map,
+ _Radix __radix,
+ _RandomAccessIterator1 __counters,
+ _RandomAccessIterator2 __maximums,
+ index_sequence<_Radices...>) {
+ using value_type = typename iterator_traits<_ForwardIterator>::value_type;
+ constexpr auto __radix_value_range = __radix_sort_traits<value_type, _Map, _Radix>::__radix_value_range;
+
+ auto __previous = numeric_limits<invoke_result_t<_Map, value_type> >::min();
+ auto __is_sorted = true;
+ std::for_each(__first, __last, [&__counters, &__map, &__radix, &__previous, &__is_sorted](const auto& value) {
+ auto __current = __map(value);
+ __is_sorted &= (__current >= __previous);
+ __previous = __current;
+
+ (++__counters[_Radices][std::__nth_radix(_Radices, __radix)(__current)], ...);
+ });
+
+ ((__maximums[_Radices] =
+ std::__partial_sum_max(__counters[_Radices], __counters[_Radices] + __radix_value_range, __counters[_Radices])
+ .second),
+ ...);
+
+ return __is_sorted;
+}
+
+template <class _ForwardIterator, class _Map, class _Radix, class _RandomAccessIterator1, class _RandomAccessIterator2>
+_LIBCPP_HIDE_FROM_ABI bool
+__collect(_ForwardIterator __first,
+ _ForwardIterator __last,
+ _Map __map,
+ _Radix __radix,
+ _RandomAccessIterator1 __counters,
+ _RandomAccessIterator2 __maximums) {
+ using value_type = typename iterator_traits<_ForwardIterator>::value_type;
+ constexpr auto __radix_count = __radix_sort_traits<value_type, _Map, _Radix>::__radix_count;
+ return std::__collect_impl(
+ __first, __last, __map, __radix, __counters, __maximums, make_index_sequence<__radix_count>());
+}
+
+template <class _BidirectionalIterator, class _RandomAccessIterator1, class _Map, class _RandomAccessIterator2>
+_LIBCPP_HIDE_FROM_ABI void __dispose_backward(
+ _BidirectionalIterator __first,
+ _BidirectionalIterator __last,
+ _RandomAccessIterator1 __result,
+ _Map __map,
+ _RandomAccessIterator2 __counters) {
+ std::for_each(std::make_reverse_iterator(__last),
+ std::make_reverse_iterator(__first),
+ [&__result, &__counters, &__map](auto&& __preimage) {
+ auto __index = --__counters[__map(__preimage)];
+ __result[__index] = std::forward<decltype(__preimage)>(__preimage);
+ });
+}
+
+template <class _ForwardIterator, class _RandomAccessIterator, class _Map>
+_LIBCPP_HIDE_FROM_ABI _RandomAccessIterator
+__counting_sort_impl(_ForwardIterator __first, _ForwardIterator __last, _RandomAccessIterator __result, _Map __map) {
+ using value_type = typename iterator_traits<_ForwardIterator>::value_type;
+ using traits = __counting_sort_traits<value_type, _Map>;
+
+ using difference_type = typename iterator_traits<_RandomAccessIterator>::difference_type;
+ difference_type __counters[traits::__value_range + 1] = {0};
+
+ std::__collect(__first, __last, __map, std::next(std::begin(__counters)));
+ std::__dispose(__first, __last, __result, __map, std::begin(__counters));
+
+ return __result + __counters[traits::__value_range];
+}
+
+template <class _RandomAccessIterator1, class _RandomAccessIterator2, class _Map, class _Radix>
+_LIBCPP_HIDE_FROM_ABI typename enable_if<
+ __radix_sort_traits<typename iterator_traits<_RandomAccessIterator1>::value_type, _Map, _Radix>::__radix_count == 1,
+ void>::type
----------------
philnik777 wrote:
You've missed a few `enable_if`s here.
https://github.com/llvm/llvm-project/pull/104683
More information about the libcxx-commits
mailing list