Skip to content

Commit e5e0cd7

Browse files
committed
[libc++] Introduce and optimise
1 parent 6464066 commit e5e0cd7

File tree

7 files changed

+194
-1
lines changed

7 files changed

+194
-1
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ set(files
498498
__iterator/ostreambuf_iterator.h
499499
__iterator/permutable.h
500500
__iterator/prev.h
501+
__iterator/product_iterator.h
501502
__iterator/projected.h
502503
__iterator/ranges_iterator_traits.h
503504
__iterator/readable_traits.h

libcxx/include/__flat_map/key_value_iterator.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <__concepts/convertible_to.h>
1515
#include <__config>
1616
#include <__iterator/iterator_traits.h>
17+
#include <__iterator/product_iterator.h>
1718
#include <__memory/addressof.h>
1819
#include <__type_traits/conditional.h>
1920
#include <__utility/move.h>
@@ -57,6 +58,8 @@ struct __key_value_iterator {
5758
template <class, class, class, bool>
5859
friend struct __key_value_iterator;
5960

61+
friend struct __product_iterator_traits<__key_value_iterator>;
62+
6063
public:
6164
using iterator_concept = random_access_iterator_tag;
6265
// `__key_value_iterator` only satisfy "Cpp17InputIterator" named requirements, because
@@ -167,6 +170,23 @@ struct __key_value_iterator {
167170
}
168171
};
169172

173+
template <class _Owner, class _KeyContainer, class _MappedContainer, bool _Const>
174+
struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const>> {
175+
static constexpr size_t __size = 2;
176+
177+
template <size_t _N>
178+
_LIBCPP_HIDE_FROM_ABI static auto
179+
__get_iterator_element(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const> __it)
180+
requires(_N == 0 || _N == 1)
181+
{
182+
if constexpr (_N == 0) {
183+
return __it.__key_iter_;
184+
} else {
185+
return __it.__mapped_iter_;
186+
}
187+
}
188+
};
189+
170190
_LIBCPP_END_NAMESPACE_STD
171191

172192
#endif // _LIBCPP_STD_VER >= 23

libcxx/include/__flat_map/utils.h

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

1313
#include <__config>
14+
#include <__iterator/product_iterator.h>
1415
#include <__type_traits/container_traits.h>
1516
#include <__utility/exception_guard.h>
1617
#include <__utility/forward.h>
@@ -93,6 +94,25 @@ struct __flat_map_utils {
9394
}
9495
return __num_appended;
9596
}
97+
98+
template <class _Map, class _InputIterator>
99+
_LIBCPP_HIDE_FROM_ABI static typename _Map::size_type
100+
__append(_Map& __map, _InputIterator __first, _InputIterator __last)
101+
requires __is_product_iterator_of_size<_InputIterator, 2>::value
102+
{
103+
auto __s1 = __map.__containers_.keys.size();
104+
__map.__containers_.keys.insert(
105+
__map.__containers_.keys.end(),
106+
__product_iterator_traits<_InputIterator>::template __get_iterator_element<0>(__first),
107+
__product_iterator_traits<_InputIterator>::template __get_iterator_element<0>(__last));
108+
109+
__map.__containers_.values.insert(
110+
__map.__containers_.values.end(),
111+
__product_iterator_traits<_InputIterator>::template __get_iterator_element<1>(__first),
112+
__product_iterator_traits<_InputIterator>::template __get_iterator_element<1>(__last));
113+
114+
return __map.__containers_.keys.size() - __s1;
115+
}
96116
};
97117
_LIBCPP_END_NAMESPACE_STD
98118

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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___PRODUCT_ITERATOR_H
10+
#define _LIBCPP___PRODUCT_ITERATOR_H
11+
12+
// Product iterators are iterators that contain two or more underlying iterators.
13+
//
14+
// For example, std::flat_map stores its data into two separate containers, and its iterator
15+
// is a proxy over two separate underlying iterators. The concept of product iterators
16+
// allows algorithms to operate over these underlying iterators separately, opening the
17+
// door to various optimizations.
18+
//
19+
// If __product_iterator_traits can be instantiated, the following functions and associated types must be provided:
20+
// - static constexpr size_t Traits::__size
21+
// The number of underlying iterators inside the product iterator.
22+
//
23+
// - template <size_t _N>
24+
// static auto Traits::__get_iterator_element(It __it)
25+
// Returns the _Nth iterator element of the given product iterator.
26+
27+
#include <__config>
28+
#include <__cstddef/size_t.h>
29+
#include <__type_traits/enable_if.h>
30+
#include <__type_traits/integral_constant.h>
31+
32+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
33+
# pragma GCC system_header
34+
#endif
35+
36+
_LIBCPP_BEGIN_NAMESPACE_STD
37+
38+
template <class _Iterator>
39+
struct __product_iterator_traits;
40+
/* exposition-only:
41+
{
42+
static constexpr size_t __size = ...;
43+
44+
template <size_t _N>
45+
static auto __get_iterator_element(_Iterator);
46+
};
47+
*/
48+
49+
template <class _Tp, size_t = 0>
50+
struct __is_product_iterator : false_type {};
51+
52+
template <class _Tp>
53+
struct __is_product_iterator<_Tp, sizeof(__product_iterator_traits<_Tp>) * 0> : true_type {};
54+
55+
template <class _Tp, size_t _Size, class = void>
56+
struct __is_product_iterator_of_size : false_type {};
57+
58+
template <class _Tp, size_t _Size>
59+
struct __is_product_iterator_of_size<_Tp, _Size, __enable_if_t<__product_iterator_traits<_Tp>::__size == _Size>>
60+
: true_type {};
61+
62+
template <class _Iterator, size_t _N>
63+
using __product_iterator_element_t =
64+
decltype(__product_iterator_traits<_Iterator>::__get_iterator_element<_N>(declval<_Iterator>()));
65+
66+
_LIBCPP_END_NAMESPACE_STD
67+
68+
#endif // _LIBCPP___PRODUCT_ITERATOR_H

libcxx/include/__ranges/zip_view.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <__iterator/iter_move.h>
2424
#include <__iterator/iter_swap.h>
2525
#include <__iterator/iterator_traits.h>
26+
#include <__iterator/product_iterator.h>
2627
#include <__ranges/access.h>
2728
#include <__ranges/all.h>
2829
#include <__ranges/concepts.h>
@@ -251,6 +252,10 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
251252

252253
friend class zip_view<_Views...>;
253254

255+
using __is_zip_view_iterator _LIBCPP_NODEBUG = true_type;
256+
257+
friend struct __product_iterator_traits<__iterator>;
258+
254259
public:
255260
using iterator_concept = decltype(ranges::__get_zip_view_iterator_tag<_Const, _Views...>());
256261
using value_type = tuple<range_value_t<__maybe_const<_Const, _Views>>...>;
@@ -468,6 +473,18 @@ inline constexpr auto zip = __zip::__fn{};
468473
} // namespace views
469474
} // namespace ranges
470475

476+
template <class _Iter>
477+
requires _Iter::__is_zip_view_iterator::value
478+
struct __product_iterator_traits<_Iter> {
479+
static constexpr size_t __size = tuple_size<decltype(std::declval<_Iter>().__current_)>::value;
480+
481+
template <size_t _N>
482+
requires(_N < __size)
483+
_LIBCPP_HIDE_FROM_ABI static constexpr auto __get_iterator_element(_Iter __it) {
484+
return std::get<_N>(__it.__current_);
485+
}
486+
};
487+
471488
#endif // _LIBCPP_STD_VER >= 23
472489

473490
_LIBCPP_END_NAMESPACE_STD

libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <flat_map>
1212
#include <utility>
13+
#include <ranges>
1314

1415
#include "associative_container_benchmarks.h"
1516
#include "../../GenerateInput.h"
@@ -26,9 +27,54 @@ struct support::adapt_operations<std::flat_map<K, V>> {
2627
static auto get_iterator(InsertionResult const& result) { return result.first; }
2728
};
2829

30+
void product_iterator_benchmark_flat_map(benchmark::State& state) {
31+
const std::size_t size = state.range(0);
32+
33+
using M = std::flat_map<int, int>;
34+
35+
const M source =
36+
std::views::iota(0, static_cast<int>(size)) | std::views::transform([](int i) { return std::pair(i, i); }) |
37+
std::ranges::to<std::flat_map<int, int>>();
38+
39+
for (auto _ : state) {
40+
M m;
41+
m.insert(std::sorted_unique, source.begin(), source.end());
42+
benchmark::DoNotOptimize(m);
43+
benchmark::ClobberMemory();
44+
}
45+
}
46+
47+
void product_iterator_benchmark_zip_view(benchmark::State& state) {
48+
const std::size_t size = state.range(0);
49+
50+
using M = std::flat_map<int, int>;
51+
52+
const std::vector<int> keys = std::views::iota(0, static_cast<int>(size)) | std::ranges::to<std::vector<int>>();
53+
const std::vector<int> values = keys;
54+
55+
auto source = std::views::zip(keys, values);
56+
for (auto _ : state) {
57+
M m;
58+
m.insert(std::sorted_unique, source.begin(), source.end());
59+
benchmark::DoNotOptimize(m);
60+
benchmark::ClobberMemory();
61+
}
62+
}
63+
2964
int main(int argc, char** argv) {
3065
support::associative_container_benchmarks<std::flat_map<int, int>>("std::flat_map<int, int>");
3166

67+
benchmark::RegisterBenchmark("flat_map::insert_product_iterator_flat_map", product_iterator_benchmark_flat_map)
68+
->Arg(32)
69+
->Arg(1024)
70+
->Arg(8192)
71+
->Arg(65536);
72+
benchmark::RegisterBenchmark("flat_map::insert_product_iterator_zip", product_iterator_benchmark_zip_view)
73+
->Arg(32)
74+
->Arg(1024)
75+
->Arg(8192)
76+
->Arg(65536);
77+
3278
benchmark::Initialize(&argc, argv);
3379
benchmark::RunSpecifiedBenchmarks();
3480
benchmark::Shutdown();

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <cassert>
1919
#include <functional>
2020
#include <deque>
21+
#include <ranges>
2122

2223
#include "MinSequenceContainer.h"
2324
#include "../helpers.h"
@@ -77,12 +78,32 @@ void test() {
7778
assert(m == expected2);
7879
}
7980

81+
void test_product_iterator() {
82+
using M = std::flat_map<int, int>;
83+
{
84+
M m1{{1, 1}, {2, 1}, {3, 1}};
85+
M m2{{4, 1}, {5, 1}, {6, 1}};
86+
m1.insert(m2.begin(), m2.end());
87+
M expected{{1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}};
88+
assert(m1 == expected);
89+
}
90+
{
91+
std::vector<int> keys{1, 2, 3};
92+
std::vector<int> values{1, 1, 1};
93+
auto zv = std::views::zip(keys, values);
94+
M m;
95+
m.insert(zv.begin(), zv.end());
96+
M expected{{1, 1}, {2, 1}, {3, 1}};
97+
assert(m == expected);
98+
}
99+
}
100+
80101
int main(int, char**) {
81102
test<std::vector<int>, std::vector<double>>();
82103
test<std::deque<int>, std::vector<double>>();
83104
test<MinSequenceContainer<int>, MinSequenceContainer<double>>();
84105
test<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();
85-
106+
test_product_iterator();
86107
{
87108
auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); };
88109
test_insert_range_exception_guarantee(insert_func);

0 commit comments

Comments
 (0)