Skip to content

Commit cb8f279

Browse files
committed
[libc++] Optimize ranges::for_each for iterating over __trees
1 parent 89eef94 commit cb8f279

File tree

8 files changed

+176
-8
lines changed

8 files changed

+176
-8
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ set(files
192192
__algorithm/shuffle.h
193193
__algorithm/sift_down.h
194194
__algorithm/simd_utils.h
195+
__algorithm/specialized_algorithms.h
195196
__algorithm/sort.h
196197
__algorithm/sort_heap.h
197198
__algorithm/stable_partition.h

libcxx/include/__algorithm/ranges_for_each.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <__algorithm/for_each.h>
1313
#include <__algorithm/for_each_n.h>
1414
#include <__algorithm/in_fun_result.h>
15+
#include <__algorithm/specialized_algorithms.h>
1516
#include <__concepts/assignable.h>
1617
#include <__config>
1718
#include <__functional/identity.h>
@@ -20,6 +21,7 @@
2021
#include <__ranges/access.h>
2122
#include <__ranges/concepts.h>
2223
#include <__ranges/dangling.h>
24+
#include <__type_traits/remove_cvref.h>
2325
#include <__utility/move.h>
2426

2527
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -71,7 +73,13 @@ struct __for_each {
7173
indirectly_unary_invocable<projected<iterator_t<_Range>, _Proj>> _Func>
7274
_LIBCPP_HIDE_FROM_ABI constexpr for_each_result<borrowed_iterator_t<_Range>, _Func>
7375
operator()(_Range&& __range, _Func __func, _Proj __proj = {}) const {
74-
return __for_each_impl(ranges::begin(__range), ranges::end(__range), __func, __proj);
76+
using _SpecialAlg = __specialized_algorithm<_Algorithm::__for_each, remove_cvref_t<_Range>>;
77+
if constexpr (_SpecialAlg::__has_algorithm) {
78+
auto [__iter, __func2] = _SpecialAlg()(__range, std::move(__func));
79+
return {std::move(__iter), std::move(__func)};
80+
} else {
81+
return __for_each_impl(ranges::begin(__range), ranges::end(__range), __func, __proj);
82+
}
7583
}
7684
};
7785

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___ALGORITHM_SPECIALIZED_ALGORITHMS_H
10+
#define _LIBCPP___ALGORITHM_SPECIALIZED_ALGORITHMS_H
11+
12+
#include <__config>
13+
14+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
15+
# pragma GCC system_header
16+
#endif
17+
18+
_LIBCPP_BEGIN_NAMESPACE_STD
19+
20+
enum class _Algorithm {
21+
__for_each,
22+
};
23+
24+
template <_Algorithm, class _Range>
25+
struct __specialized_algorithm {
26+
static const bool __has_algorithm = false;
27+
};
28+
29+
_LIBCPP_END_NAMESPACE_STD
30+
31+
#endif // _LIBCPP___ALGORITHM_SPECIALIZED_ALGORITHMS_H

libcxx/include/__tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define _LIBCPP___TREE
1212

1313
#include <__algorithm/min.h>
14+
#include <__algorithm/specialized_algorithms.h>
1415
#include <__assert>
1516
#include <__config>
1617
#include <__fwd/pair.h>
@@ -1466,8 +1467,38 @@ private:
14661467

14671468
return __dest;
14681469
}
1470+
1471+
friend struct __specialized_algorithm<_Algorithm::__for_each, __tree>;
14691472
};
14701473

1474+
#ifndef _LIBCPP_CXX03_LANG
1475+
template <class _Tp, class _Compare, class _Allocator>
1476+
struct __specialized_algorithm<_Algorithm::__for_each, __tree<_Tp, _Compare, _Allocator> > {
1477+
static const bool __has_algorithm = true;
1478+
1479+
using __tree _LIBCPP_NODEBUG = __tree<_Tp, _Compare, _Allocator>;
1480+
using __node_pointer _LIBCPP_NODEBUG = typename __tree::__node_pointer;
1481+
1482+
template <class _Func>
1483+
#ifndef _LIBCPP_COMPILER_GCC
1484+
_LIBCPP_HIDE_FROM_ABI
1485+
#endif
1486+
static void __impl(__node_pointer __root, _Func& __func) {
1487+
if (__root->__left_)
1488+
__impl(static_cast<__node_pointer>(__root->__left_), __func);
1489+
__func(__root->__get_value());
1490+
if (__root->__right_)
1491+
__impl(static_cast<__node_pointer>(__root->__right_), __func);
1492+
}
1493+
1494+
template <class _Tree, class _Func>
1495+
_LIBCPP_HIDE_FROM_ABI static auto operator()(_Tree&& __range, _Func __func) {
1496+
__impl(__range.__root(), __func);
1497+
return std::make_pair(__range.end(), std::move(__func));
1498+
}
1499+
};
1500+
#endif
1501+
14711502
// Precondition: __size_ != 0
14721503
template <class _Tp, class _Compare, class _Allocator>
14731504
typename __tree<_Tp, _Compare, _Allocator>::__node_pointer

libcxx/include/map

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred); // C++20
577577
# include <__algorithm/equal.h>
578578
# include <__algorithm/lexicographical_compare.h>
579579
# include <__algorithm/lexicographical_compare_three_way.h>
580+
# include <__algorithm/specialized_algorithms.h>
580581
# include <__assert>
581582
# include <__config>
582583
# include <__functional/binary_function.h>
@@ -1375,6 +1376,8 @@ private:
13751376
# ifdef _LIBCPP_CXX03_LANG
13761377
_LIBCPP_HIDE_FROM_ABI __node_holder __construct_node_with_key(const key_type& __k);
13771378
# endif
1379+
1380+
friend struct __specialized_algorithm<_Algorithm::__for_each, map>;
13781381
};
13791382

13801383
# if _LIBCPP_STD_VER >= 17
@@ -1427,6 +1430,23 @@ map(initializer_list<pair<_Key, _Tp>>, _Allocator)
14271430
-> map<remove_const_t<_Key>, _Tp, less<remove_const_t<_Key>>, _Allocator>;
14281431
# endif
14291432

1433+
# if _LIBCPP_STD_VER >= 14
1434+
template <class _Key, class _Tp, class _Compare, class _Allocator>
1435+
struct __specialized_algorithm<_Algorithm::__for_each, map<_Key, _Tp, _Compare, _Allocator>> {
1436+
using __map _LIBCPP_NODEBUG = map<_Key, _Tp, _Compare, _Allocator>;
1437+
1438+
static const bool __has_algorithm = true;
1439+
1440+
// set's begin() and end() are identical with and without const qualifiaction
1441+
template <class _Map, class _Func>
1442+
_LIBCPP_HIDE_FROM_ABI static auto operator()(_Map&& __map, _Func __func) {
1443+
auto [_, __func2] = __specialized_algorithm<_Algorithm::__for_each, typename __map::__base>()(
1444+
__map.__tree_, std::move(__func));
1445+
return std::make_pair(__map.end(), std::move(__func2));
1446+
}
1447+
};
1448+
# endif
1449+
14301450
# ifndef _LIBCPP_CXX03_LANG
14311451
template <class _Key, class _Tp, class _Compare, class _Allocator>
14321452
map<_Key, _Tp, _Compare, _Allocator>::map(map&& __m, const allocator_type& __a)
@@ -1940,6 +1960,8 @@ private:
19401960

19411961
typedef __map_node_destructor<__node_allocator> _Dp;
19421962
typedef unique_ptr<__node, _Dp> __node_holder;
1963+
1964+
friend struct __specialized_algorithm<_Algorithm::__for_each, multimap>;
19431965
};
19441966

19451967
# if _LIBCPP_STD_VER >= 17
@@ -1992,6 +2014,23 @@ multimap(initializer_list<pair<_Key, _Tp>>, _Allocator)
19922014
-> multimap<remove_const_t<_Key>, _Tp, less<remove_const_t<_Key>>, _Allocator>;
19932015
# endif
19942016

2017+
# if _LIBCPP_STD_VER >= 14
2018+
template <class _Key, class _Tp, class _Compare, class _Allocator>
2019+
struct __specialized_algorithm<_Algorithm::__for_each, multimap<_Key, _Tp, _Compare, _Allocator>> {
2020+
using __map _LIBCPP_NODEBUG = multimap<_Key, _Tp, _Compare, _Allocator>;
2021+
2022+
static const bool __has_algorithm = true;
2023+
2024+
// set's begin() and end() are identical with and without const qualifiaction
2025+
template <class _Map, class _Func>
2026+
_LIBCPP_HIDE_FROM_ABI static auto operator()(_Map&& __map, _Func __func) {
2027+
auto [_, __func2] = __specialized_algorithm<_Algorithm::__for_each, typename __map::__base>()(
2028+
__map.__tree_, std::move(__func));
2029+
return std::make_pair(__map.end(), std::move(__func2));
2030+
}
2031+
};
2032+
# endif
2033+
19952034
# ifndef _LIBCPP_CXX03_LANG
19962035
template <class _Key, class _Tp, class _Compare, class _Allocator>
19972036
multimap<_Key, _Tp, _Compare, _Allocator>::multimap(multimap&& __m, const allocator_type& __a)

libcxx/include/module.modulemap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,7 @@ module std [system] {
844844
export std.memory.unique_temporary_buffer // TODO: Workaround for https://llvm.org/PR120108
845845
}
846846
module swap_ranges { header "__algorithm/swap_ranges.h" }
847+
module specialized_algorithms { header "__algorithm/specialized_algorithms.h" }
847848
module three_way_comp_ref_type { header "__algorithm/three_way_comp_ref_type.h" }
848849
module transform { header "__algorithm/transform.h" }
849850
module uniform_random_bit_generator_adaptor { header "__algorithm/uniform_random_bit_generator_adaptor.h" }

libcxx/include/set

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred); // C++20
518518
# include <__algorithm/equal.h>
519519
# include <__algorithm/lexicographical_compare.h>
520520
# include <__algorithm/lexicographical_compare_three_way.h>
521+
# include <__algorithm/specialized_algorithms.h>
521522
# include <__assert>
522523
# include <__config>
523524
# include <__functional/is_transparent.h>
@@ -902,6 +903,9 @@ public:
902903
return __tree_.__equal_range_multi(__k);
903904
}
904905
# endif
906+
907+
template <_Algorithm, class>
908+
friend struct __specialized_algorithm;
905909
};
906910

907911
# if _LIBCPP_STD_VER >= 17
@@ -948,6 +952,21 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
948952
set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
949953
# endif
950954

955+
# if _LIBCPP_STD_VER >= 14
956+
template <_Algorithm __alg, class _Key, class _Compare, class _Allocator>
957+
struct __specialized_algorithm<__alg, set<_Key, _Compare, _Allocator>> {
958+
using __set _LIBCPP_NODEBUG = set<_Key, _Compare, _Allocator>;
959+
960+
static const bool __has_algorithm = __specialized_algorithm<__alg, typename __set::__base>::__has_algorithm;
961+
962+
// set's begin() and end() are identical with and without const qualifiaction
963+
template <class... _Args>
964+
_LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
965+
return __specialized_algorithm<__alg, typename __set::__base>()(__set.__tree_, std::forward<_Args>(__args)...);
966+
}
967+
};
968+
# endif
969+
951970
# ifndef _LIBCPP_CXX03_LANG
952971

953972
template <class _Key, class _Compare, class _Allocator>
@@ -1362,6 +1381,9 @@ public:
13621381
return __tree_.__equal_range_multi(__k);
13631382
}
13641383
# endif
1384+
1385+
template <_Algorithm, class>
1386+
friend struct __specialized_algorithm;
13651387
};
13661388

13671389
# if _LIBCPP_STD_VER >= 17
@@ -1409,6 +1431,21 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
14091431
multiset(initializer_list<_Key>, _Allocator) -> multiset<_Key, less<_Key>, _Allocator>;
14101432
# endif
14111433

1434+
# if _LIBCPP_STD_VER >= 14
1435+
template <_Algorithm __alg, class _Key, class _Compare, class _Allocator>
1436+
struct __specialized_algorithm<__alg, multiset<_Key, _Compare, _Allocator>> {
1437+
using __set _LIBCPP_NODEBUG = multiset<_Key, _Compare, _Allocator>;
1438+
1439+
static const bool __has_algorithm = __specialized_algorithm<__alg, typename __set::__base>::__has_algorithm;
1440+
1441+
// set's begin() and end() are identical with and without const qualifiaction
1442+
template <class... _Args>
1443+
_LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
1444+
return __specialized_algorithm<__alg, typename __set::__base>()(__set.__tree_, std::forward<_Args>(__args)...);
1445+
}
1446+
};
1447+
# endif
1448+
14121449
# ifndef _LIBCPP_CXX03_LANG
14131450

14141451
template <class _Key, class _Compare, class _Allocator>

libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ int main(int argc, char** argv) {
2323

2424
// {std,ranges}::for_each
2525
{
26-
auto bm = []<class Container>(std::string name, auto for_each) {
26+
auto sequence_bm = []<class Container>(std::string name, auto for_each) {
2727
using ElemType = typename Container::value_type;
2828
benchmark::RegisterBenchmark(
2929
name,
@@ -44,12 +44,32 @@ int main(int argc, char** argv) {
4444
->Arg(50) // non power-of-two
4545
->Arg(8192);
4646
};
47-
bm.operator()<std::vector<int>>("std::for_each(vector<int>)", std_for_each);
48-
bm.operator()<std::deque<int>>("std::for_each(deque<int>)", std_for_each);
49-
bm.operator()<std::list<int>>("std::for_each(list<int>)", std_for_each);
50-
bm.operator()<std::vector<int>>("rng::for_each(vector<int>)", std::ranges::for_each);
51-
bm.operator()<std::deque<int>>("rng::for_each(deque<int>)", std::ranges::for_each);
52-
bm.operator()<std::list<int>>("rng::for_each(list<int>)", std::ranges::for_each);
47+
sequence_bm.operator()<std::vector<int>>("std::for_each(vector<int>)", std_for_each);
48+
sequence_bm.operator()<std::deque<int>>("std::for_each(deque<int>)", std_for_each);
49+
sequence_bm.operator()<std::list<int>>("std::for_each(list<int>)", std_for_each);
50+
sequence_bm.operator()<std::vector<int>>("rng::for_each(vector<int>)", std::ranges::for_each);
51+
sequence_bm.operator()<std::deque<int>>("rng::for_each(deque<int>)", std::ranges::for_each);
52+
sequence_bm.operator()<std::list<int>>("rng::for_each(list<int>)", std::ranges::for_each);
53+
54+
auto associative_ranges_bm = []<class Container>(std::type_identity<Container>, std::string name, auto for_each) {
55+
benchmark::RegisterBenchmark(
56+
name,
57+
[for_each](auto& st) {
58+
Container c;
59+
for (int64_t i = 0; i != st.range(0); ++i)
60+
c.insert(i);
61+
62+
for (auto _ : st) {
63+
benchmark::DoNotOptimize(c);
64+
for_each(c, [](auto v) { benchmark::DoNotOptimize(v); });
65+
}
66+
})
67+
->Arg(8)
68+
->Arg(32)
69+
->Arg(50) // non power-of-two
70+
->Arg(8192);
71+
};
72+
associative_ranges_bm(std::type_identity<std::set<int>>{}, "rng::for_each(set<int>)", std::ranges::for_each);
5373
}
5474

5575
// {std,ranges}::for_each for join_view

0 commit comments

Comments
 (0)