[libcxx-commits] [libcxx] [libc++] Introduce `__product_iterator_traits` and optimise `flat_map::insert` (PR #139454)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jun 7 23:50:22 PDT 2025
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/139454
>From 28c696ed3ba0f3fcc571c4e8d627a26465ee753a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 11 May 2025 16:39:34 +0100
Subject: [PATCH 01/10] [libc++] Introduce and optimise
---
libcxx/include/CMakeLists.txt | 1 +
.../include/__flat_map/key_value_iterator.h | 20 ++++++
libcxx/include/__flat_map/utils.h | 20 ++++++
libcxx/include/__iterator/product_iterator.h | 68 +++++++++++++++++++
libcxx/include/__ranges/zip_view.h | 17 +++++
.../containers/associative/flat_map.bench.cpp | 46 +++++++++++++
.../insert_iter_iter.pass.cpp | 23 ++++++-
7 files changed, 194 insertions(+), 1 deletion(-)
create mode 100644 libcxx/include/__iterator/product_iterator.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 43cefd5600646..11bae8c9458a0 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -498,6 +498,7 @@ set(files
__iterator/ostreambuf_iterator.h
__iterator/permutable.h
__iterator/prev.h
+ __iterator/product_iterator.h
__iterator/projected.h
__iterator/ranges_iterator_traits.h
__iterator/readable_traits.h
diff --git a/libcxx/include/__flat_map/key_value_iterator.h b/libcxx/include/__flat_map/key_value_iterator.h
index 3ebb653deb197..968d354de8350 100644
--- a/libcxx/include/__flat_map/key_value_iterator.h
+++ b/libcxx/include/__flat_map/key_value_iterator.h
@@ -14,6 +14,7 @@
#include <__concepts/convertible_to.h>
#include <__config>
#include <__iterator/iterator_traits.h>
+#include <__iterator/product_iterator.h>
#include <__memory/addressof.h>
#include <__type_traits/conditional.h>
#include <__utility/move.h>
@@ -57,6 +58,8 @@ struct __key_value_iterator {
template <class, class, class, bool>
friend struct __key_value_iterator;
+ friend struct __product_iterator_traits<__key_value_iterator>;
+
public:
using iterator_concept = random_access_iterator_tag;
// `__key_value_iterator` only satisfy "Cpp17InputIterator" named requirements, because
@@ -167,6 +170,23 @@ struct __key_value_iterator {
}
};
+template <class _Owner, class _KeyContainer, class _MappedContainer, bool _Const>
+struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const>> {
+ static constexpr size_t __size = 2;
+
+ template <size_t _N>
+ _LIBCPP_HIDE_FROM_ABI static auto
+ __get_iterator_element(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const> __it)
+ requires(_N == 0 || _N == 1)
+ {
+ if constexpr (_N == 0) {
+ return __it.__key_iter_;
+ } else {
+ return __it.__mapped_iter_;
+ }
+ }
+};
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/__flat_map/utils.h b/libcxx/include/__flat_map/utils.h
index acb7dca7ffe96..2c0e6ada31a5e 100644
--- a/libcxx/include/__flat_map/utils.h
+++ b/libcxx/include/__flat_map/utils.h
@@ -11,6 +11,7 @@
#define _LIBCPP___FLAT_MAP_UTILS_H
#include <__config>
+#include <__iterator/product_iterator.h>
#include <__type_traits/container_traits.h>
#include <__utility/exception_guard.h>
#include <__utility/forward.h>
@@ -93,6 +94,25 @@ struct __flat_map_utils {
}
return __num_appended;
}
+
+ template <class _Map, class _InputIterator>
+ _LIBCPP_HIDE_FROM_ABI static typename _Map::size_type
+ __append(_Map& __map, _InputIterator __first, _InputIterator __last)
+ requires __is_product_iterator_of_size<_InputIterator, 2>::value
+ {
+ auto __s1 = __map.__containers_.keys.size();
+ __map.__containers_.keys.insert(
+ __map.__containers_.keys.end(),
+ __product_iterator_traits<_InputIterator>::template __get_iterator_element<0>(__first),
+ __product_iterator_traits<_InputIterator>::template __get_iterator_element<0>(__last));
+
+ __map.__containers_.values.insert(
+ __map.__containers_.values.end(),
+ __product_iterator_traits<_InputIterator>::template __get_iterator_element<1>(__first),
+ __product_iterator_traits<_InputIterator>::template __get_iterator_element<1>(__last));
+
+ return __map.__containers_.keys.size() - __s1;
+ }
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__iterator/product_iterator.h b/libcxx/include/__iterator/product_iterator.h
new file mode 100644
index 0000000000000..70b7acce456fb
--- /dev/null
+++ b/libcxx/include/__iterator/product_iterator.h
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PRODUCT_ITERATOR_H
+#define _LIBCPP___PRODUCT_ITERATOR_H
+
+// Product iterators are iterators that contain two or more underlying iterators.
+//
+// For example, std::flat_map stores its data into two separate containers, and its iterator
+// is a proxy over two separate underlying iterators. The concept of product iterators
+// allows algorithms to operate over these underlying iterators separately, opening the
+// door to various optimizations.
+//
+// If __product_iterator_traits can be instantiated, the following functions and associated types must be provided:
+// - static constexpr size_t Traits::__size
+// The number of underlying iterators inside the product iterator.
+//
+// - template <size_t _N>
+// static auto Traits::__get_iterator_element(It __it)
+// Returns the _Nth iterator element of the given product iterator.
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Iterator>
+struct __product_iterator_traits;
+/* exposition-only:
+{
+ static constexpr size_t __size = ...;
+
+ template <size_t _N>
+ static auto __get_iterator_element(_Iterator);
+};
+*/
+
+template <class _Tp, size_t = 0>
+struct __is_product_iterator : false_type {};
+
+template <class _Tp>
+struct __is_product_iterator<_Tp, sizeof(__product_iterator_traits<_Tp>) * 0> : true_type {};
+
+template <class _Tp, size_t _Size, class = void>
+struct __is_product_iterator_of_size : false_type {};
+
+template <class _Tp, size_t _Size>
+struct __is_product_iterator_of_size<_Tp, _Size, __enable_if_t<__product_iterator_traits<_Tp>::__size == _Size>>
+ : true_type {};
+
+template <class _Iterator, size_t _N>
+using __product_iterator_element_t =
+ decltype(__product_iterator_traits<_Iterator>::__get_iterator_element<_N>(declval<_Iterator>()));
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___PRODUCT_ITERATOR_H
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index 6d31f5ce0993c..b074e69832535 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -23,6 +23,7 @@
#include <__iterator/iter_move.h>
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/product_iterator.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
@@ -251,6 +252,10 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
friend class zip_view<_Views...>;
+ using __is_zip_view_iterator _LIBCPP_NODEBUG = true_type;
+
+ friend struct __product_iterator_traits<__iterator>;
+
public:
using iterator_concept = decltype(ranges::__get_zip_view_iterator_tag<_Const, _Views...>());
using value_type = tuple<range_value_t<__maybe_const<_Const, _Views>>...>;
@@ -468,6 +473,18 @@ inline constexpr auto zip = __zip::__fn{};
} // namespace views
} // namespace ranges
+template <class _Iter>
+ requires _Iter::__is_zip_view_iterator::value
+struct __product_iterator_traits<_Iter> {
+ static constexpr size_t __size = tuple_size<decltype(std::declval<_Iter>().__current_)>::value;
+
+ template <size_t _N>
+ requires(_N < __size)
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __get_iterator_element(_Iter __it) {
+ return std::get<_N>(__it.__current_);
+ }
+};
+
#endif // _LIBCPP_STD_VER >= 23
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
index 82902d50f31e6..7351e79758fa8 100644
--- a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
@@ -10,6 +10,7 @@
#include <flat_map>
#include <utility>
+#include <ranges>
#include "associative_container_benchmarks.h"
#include "../../GenerateInput.h"
@@ -26,9 +27,54 @@ struct support::adapt_operations<std::flat_map<K, V>> {
static auto get_iterator(InsertionResult const& result) { return result.first; }
};
+void product_iterator_benchmark_flat_map(benchmark::State& state) {
+ const std::size_t size = state.range(0);
+
+ using M = std::flat_map<int, int>;
+
+ const M source =
+ std::views::iota(0, static_cast<int>(size)) | std::views::transform([](int i) { return std::pair(i, i); }) |
+ std::ranges::to<std::flat_map<int, int>>();
+
+ for (auto _ : state) {
+ M m;
+ m.insert(std::sorted_unique, source.begin(), source.end());
+ benchmark::DoNotOptimize(m);
+ benchmark::ClobberMemory();
+ }
+}
+
+void product_iterator_benchmark_zip_view(benchmark::State& state) {
+ const std::size_t size = state.range(0);
+
+ using M = std::flat_map<int, int>;
+
+ const std::vector<int> keys = std::views::iota(0, static_cast<int>(size)) | std::ranges::to<std::vector<int>>();
+ const std::vector<int> values = keys;
+
+ auto source = std::views::zip(keys, values);
+ for (auto _ : state) {
+ M m;
+ m.insert(std::sorted_unique, source.begin(), source.end());
+ benchmark::DoNotOptimize(m);
+ benchmark::ClobberMemory();
+ }
+}
+
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::flat_map<int, int>>("std::flat_map<int, int>");
+ benchmark::RegisterBenchmark("flat_map::insert_product_iterator_flat_map", product_iterator_benchmark_flat_map)
+ ->Arg(32)
+ ->Arg(1024)
+ ->Arg(8192)
+ ->Arg(65536);
+ benchmark::RegisterBenchmark("flat_map::insert_product_iterator_zip", product_iterator_benchmark_zip_view)
+ ->Arg(32)
+ ->Arg(1024)
+ ->Arg(8192)
+ ->Arg(65536);
+
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp
index ccce117c90fca..20d0066dbaa15 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp
@@ -18,6 +18,7 @@
#include <cassert>
#include <functional>
#include <deque>
+#include <ranges>
#include "MinSequenceContainer.h"
#include "../helpers.h"
@@ -77,12 +78,32 @@ void test() {
assert(m == expected2);
}
+void test_product_iterator() {
+ using M = std::flat_map<int, int>;
+ {
+ M m1{{1, 1}, {2, 1}, {3, 1}};
+ M m2{{4, 1}, {5, 1}, {6, 1}};
+ m1.insert(m2.begin(), m2.end());
+ M expected{{1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}};
+ assert(m1 == expected);
+ }
+ {
+ std::vector<int> keys{1, 2, 3};
+ std::vector<int> values{1, 1, 1};
+ auto zv = std::views::zip(keys, values);
+ M m;
+ m.insert(zv.begin(), zv.end());
+ M expected{{1, 1}, {2, 1}, {3, 1}};
+ assert(m == expected);
+ }
+}
+
int main(int, char**) {
test<std::vector<int>, std::vector<double>>();
test<std::deque<int>, std::vector<double>>();
test<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();
-
+ test_product_iterator();
{
auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); };
test_insert_range_exception_guarantee(insert_func);
>From 0e0ecf651d46888f30a063be2158ad501e6e669c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 11 May 2025 19:08:03 +0100
Subject: [PATCH 02/10] CI
---
libcxx/include/__flat_map/key_value_iterator.h | 6 +++---
libcxx/include/__iterator/product_iterator.h | 9 +++++----
libcxx/include/__ranges/zip_view.h | 6 +++---
3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/libcxx/include/__flat_map/key_value_iterator.h b/libcxx/include/__flat_map/key_value_iterator.h
index 968d354de8350..130e6413bdecc 100644
--- a/libcxx/include/__flat_map/key_value_iterator.h
+++ b/libcxx/include/__flat_map/key_value_iterator.h
@@ -174,12 +174,12 @@ template <class _Owner, class _KeyContainer, class _MappedContainer, bool _Const
struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const>> {
static constexpr size_t __size = 2;
- template <size_t _N>
+ template <size_t _Nth>
_LIBCPP_HIDE_FROM_ABI static auto
__get_iterator_element(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const> __it)
- requires(_N == 0 || _N == 1)
+ requires(_Nth <= 1)
{
- if constexpr (_N == 0) {
+ if constexpr (_Nth == 0) {
return __it.__key_iter_;
} else {
return __it.__mapped_iter_;
diff --git a/libcxx/include/__iterator/product_iterator.h b/libcxx/include/__iterator/product_iterator.h
index 70b7acce456fb..2522f292fe6d7 100644
--- a/libcxx/include/__iterator/product_iterator.h
+++ b/libcxx/include/__iterator/product_iterator.h
@@ -28,6 +28,7 @@
#include <__cstddef/size_t.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/integral_constant.h>
+#include <__utility/declval.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -56,12 +57,12 @@ template <class _Tp, size_t _Size, class = void>
struct __is_product_iterator_of_size : false_type {};
template <class _Tp, size_t _Size>
-struct __is_product_iterator_of_size<_Tp, _Size, __enable_if_t<__product_iterator_traits<_Tp>::__size == _Size>>
+struct __is_product_iterator_of_size<_Tp, _Size, __enable_if_t<__product_iterator_traits<_Tp>::__size == _Size> >
: true_type {};
-template <class _Iterator, size_t _N>
-using __product_iterator_element_t =
- decltype(__product_iterator_traits<_Iterator>::__get_iterator_element<_N>(declval<_Iterator>()));
+template <class _Iterator, size_t _Nth>
+using __product_iterator_element_t _LIBCPP_NODEBUG =
+ decltype(__product_iterator_traits<_Iterator>::__get_iterator_element<_Nth>(std::declval<_Iterator>()));
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index b074e69832535..0f25f3ad8be56 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -478,10 +478,10 @@ template <class _Iter>
struct __product_iterator_traits<_Iter> {
static constexpr size_t __size = tuple_size<decltype(std::declval<_Iter>().__current_)>::value;
- template <size_t _N>
- requires(_N < __size)
+ template <size_t _Nth>
+ requires(_Nth < __size)
_LIBCPP_HIDE_FROM_ABI static constexpr auto __get_iterator_element(_Iter __it) {
- return std::get<_N>(__it.__current_);
+ return std::get<_Nth>(__it.__current_);
}
};
>From 97f1b61af35c0c1ac9ce2a779cb92eaed5c12815 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 11 May 2025 19:20:07 +0100
Subject: [PATCH 03/10] CI
---
libcxx/include/module.modulemap.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 7f625cefed1c2..be17831cea70e 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1526,6 +1526,7 @@ module std [system] {
}
module permutable { header "__iterator/permutable.h" }
module prev { header "__iterator/prev.h" }
+ module product_iterator { header "__iterator/product_iterator.h" }
module projected { header "__iterator/projected.h" }
module ranges_iterator_traits { header "__iterator/ranges_iterator_traits.h" }
module readable_traits { header "__iterator/readable_traits.h" }
>From 1d07dd9e41de10fd026ce1292aca929562c55702 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 11 May 2025 19:43:57 +0100
Subject: [PATCH 04/10] CI
---
libcxx/include/__flat_map/key_value_iterator.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__flat_map/key_value_iterator.h b/libcxx/include/__flat_map/key_value_iterator.h
index 130e6413bdecc..085b52c277203 100644
--- a/libcxx/include/__flat_map/key_value_iterator.h
+++ b/libcxx/include/__flat_map/key_value_iterator.h
@@ -13,6 +13,7 @@
#include <__compare/three_way_comparable.h>
#include <__concepts/convertible_to.h>
#include <__config>
+#include <__cstddef/size_t.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/product_iterator.h>
#include <__memory/addressof.h>
>From b6bef80c11d45d31ad8ac2dd64656f0d4c5adf4f Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 17 May 2025 16:46:39 +0100
Subject: [PATCH 05/10] Apply suggestions from code review
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
libcxx/include/__iterator/product_iterator.h | 6 +++---
libcxx/include/__ranges/zip_view.h | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__iterator/product_iterator.h b/libcxx/include/__iterator/product_iterator.h
index 2522f292fe6d7..82dd6b1e7938f 100644
--- a/libcxx/include/__iterator/product_iterator.h
+++ b/libcxx/include/__iterator/product_iterator.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP___PRODUCT_ITERATOR_H
-#define _LIBCPP___PRODUCT_ITERATOR_H
+#ifndef _LIBCPP___ITERATOR_PRODUCT_ITERATOR_H
+#define _LIBCPP___ITERATOR_PRODUCT_ITERATOR_H
// Product iterators are iterators that contain two or more underlying iterators.
//
@@ -66,4 +66,4 @@ using __product_iterator_element_t _LIBCPP_NODEBUG =
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP___PRODUCT_ITERATOR_H
+#endif // _LIBCPP___ITERATOR_PRODUCT_ITERATOR_H
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index 0f25f3ad8be56..b703f8f05c61a 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -252,7 +252,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
friend class zip_view<_Views...>;
- using __is_zip_view_iterator _LIBCPP_NODEBUG = true_type;
+ static constexpr bool __is_zip_view_iterator = true;
friend struct __product_iterator_traits<__iterator>;
>From b5e6a91bcb489286fa68413ca172603522c60bd1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 18 May 2025 11:27:24 +0100
Subject: [PATCH 06/10] review comments
---
.../include/__flat_map/key_value_iterator.h | 16 +++++--
libcxx/include/__flat_map/utils.h | 2 -
libcxx/include/__iterator/product_iterator.h | 9 ++--
libcxx/include/__ranges/zip_view.h | 19 +++++---
.../associative_container_benchmarks.h | 43 +++++++++++++++++
.../containers/associative/flat_map.bench.cpp | 45 ------------------
.../iterators/product_iterator.pass.cpp | 46 +++++++++++++++++++
7 files changed, 118 insertions(+), 62 deletions(-)
create mode 100644 libcxx/test/libcxx/iterators/product_iterator.pass.cpp
diff --git a/libcxx/include/__flat_map/key_value_iterator.h b/libcxx/include/__flat_map/key_value_iterator.h
index 085b52c277203..2f53ffdab3f20 100644
--- a/libcxx/include/__flat_map/key_value_iterator.h
+++ b/libcxx/include/__flat_map/key_value_iterator.h
@@ -18,6 +18,7 @@
#include <__iterator/product_iterator.h>
#include <__memory/addressof.h>
#include <__type_traits/conditional.h>
+#include <__utility/forward.h>
#include <__utility/move.h>
#include <__utility/pair.h>
@@ -175,17 +176,22 @@ template <class _Owner, class _KeyContainer, class _MappedContainer, bool _Const
struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const>> {
static constexpr size_t __size = 2;
- template <size_t _Nth>
- _LIBCPP_HIDE_FROM_ABI static auto
- __get_iterator_element(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const> __it)
+ template <size_t _Nth, class _Iter>
+ _LIBCPP_HIDE_FROM_ABI static decltype(auto) __get_iterator_element(_Iter&& __it)
requires(_Nth <= 1)
{
if constexpr (_Nth == 0) {
- return __it.__key_iter_;
+ return std::forward<_Iter>(__it).__key_iter_;
} else {
- return __it.__mapped_iter_;
+ return std::forward<_Iter>(__it).__mapped_iter_;
}
}
+
+ template <class _KeyIter, class _MappedIter>
+ _LIBCPP_HIDE_FROM_ABI static auto __make_product_iterator(_KeyIter&& __key_iter, _MappedIter&& __mapped_iter) {
+ return __key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const>(
+ std::forward<_KeyIter>(__key_iter), std::forward<_MappedIter>(__mapped_iter));
+ }
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__flat_map/utils.h b/libcxx/include/__flat_map/utils.h
index 2c0e6ada31a5e..74d19858ddea9 100644
--- a/libcxx/include/__flat_map/utils.h
+++ b/libcxx/include/__flat_map/utils.h
@@ -80,8 +80,6 @@ struct __flat_map_utils {
return typename _Map::iterator(std::move(__key_it), std::move(__mapped_it));
}
- // TODO: We could optimize this, see
- // https://github.com/llvm/llvm-project/issues/108624
template <class _Map, class _InputIterator, class _Sentinel>
_LIBCPP_HIDE_FROM_ABI static typename _Map::size_type
__append(_Map& __map, _InputIterator __first, _Sentinel __last) {
diff --git a/libcxx/include/__iterator/product_iterator.h b/libcxx/include/__iterator/product_iterator.h
index 82dd6b1e7938f..11f340621e128 100644
--- a/libcxx/include/__iterator/product_iterator.h
+++ b/libcxx/include/__iterator/product_iterator.h
@@ -21,7 +21,7 @@
// The number of underlying iterators inside the product iterator.
//
// - template <size_t _N>
-// static auto Traits::__get_iterator_element(It __it)
+// static decltype(auto) Traits::__get_iterator_element(It&& __it)
// Returns the _Nth iterator element of the given product iterator.
#include <__config>
@@ -42,8 +42,11 @@ struct __product_iterator_traits;
{
static constexpr size_t __size = ...;
- template <size_t _N>
- static auto __get_iterator_element(_Iterator);
+ template <size_t _N, class _Iter>
+ static decltype(auto) __get_iterator_element(_Iter&&);
+
+ template <class... _Iters>
+ static _Iterator __make_product_iterator(_Iters&&...);
};
*/
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index b703f8f05c61a..e2a194efcfb4c 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -473,15 +473,20 @@ inline constexpr auto zip = __zip::__fn{};
} // namespace views
} // namespace ranges
-template <class _Iter>
- requires _Iter::__is_zip_view_iterator::value
-struct __product_iterator_traits<_Iter> {
- static constexpr size_t __size = tuple_size<decltype(std::declval<_Iter>().__current_)>::value;
+template <class _Iterator>
+ requires _Iterator::__is_zip_view_iterator
+struct __product_iterator_traits<_Iterator> {
+ static constexpr size_t __size = tuple_size<decltype(std::declval<_Iterator>().__current_)>::value;
- template <size_t _Nth>
+ template <size_t _Nth, class _Iter>
requires(_Nth < __size)
- _LIBCPP_HIDE_FROM_ABI static constexpr auto __get_iterator_element(_Iter __it) {
- return std::get<_Nth>(__it.__current_);
+ _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_iterator_element(_Iter&& __it) {
+ return std::get<_Nth>(std::forward<_Iter>(__it).__current_);
+ }
+
+ template <class... _Iters>
+ _LIBCPP_HIDE_FROM_ABI static constexpr _Iterator __make_product_iterator(_Iters&&... __iters) {
+ return _Iterator(std::tuple(std::forward<_Iters>(__iters)...));
}
};
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index fb4455c4aa9da..098e70102e343 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -66,6 +66,8 @@ void associative_container_benchmarks(std::string container) {
static constexpr bool is_ordered_container = requires(Container c, Key k) { c.lower_bound(k); };
+ static constexpr bool is_map_like = requires { typename Container::mapped_type; };
+
// These benchmarks are structured to perform the operation being benchmarked
// a small number of times at each iteration, in order to offset the cost of
// PauseTiming() and ResumeTiming().
@@ -321,6 +323,47 @@ void associative_container_benchmarks(std::string container) {
}
});
+ if constexpr (is_map_like) {
+ bench("insert(iterator, iterator) (product_iterator from same type)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + (size / 10)));
+ std::sort(in.begin(), in.end(), [&](const auto& x, const auto& y) { return get_key(x) < get_key(y); });
+ Container source(in.begin(), in.end());
+
+ Container c;
+
+ for ([[maybe_unused]] auto _ : st) {
+ c.insert(source.begin(), source.end());
+ benchmark::DoNotOptimize(c);
+ benchmark::ClobberMemory();
+
+ st.PauseTiming();
+ c = Container();
+ st.ResumeTiming();
+ }
+ });
+
+ bench("insert(iterator, iterator) (product_iterator from zip_view)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Key> keys = generate_unique_keys(size + (size / 10));
+ std::sort(keys.begin(), keys.end());
+ std::vector<typename Container::mapped_type> mapped(keys.size());
+
+ auto source = std::views::zip(keys, mapped);
+
+ Container c;
+
+ for ([[maybe_unused]] auto _ : st) {
+ c.insert(source.begin(), source.end());
+ benchmark::DoNotOptimize(c);
+ benchmark::ClobberMemory();
+
+ st.PauseTiming();
+ c = Container();
+ st.ResumeTiming();
+ }
+ });
+ }
/////////////////////////
// Erasure
/////////////////////////
diff --git a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
index 7351e79758fa8..034602879be1b 100644
--- a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
@@ -27,54 +27,9 @@ struct support::adapt_operations<std::flat_map<K, V>> {
static auto get_iterator(InsertionResult const& result) { return result.first; }
};
-void product_iterator_benchmark_flat_map(benchmark::State& state) {
- const std::size_t size = state.range(0);
-
- using M = std::flat_map<int, int>;
-
- const M source =
- std::views::iota(0, static_cast<int>(size)) | std::views::transform([](int i) { return std::pair(i, i); }) |
- std::ranges::to<std::flat_map<int, int>>();
-
- for (auto _ : state) {
- M m;
- m.insert(std::sorted_unique, source.begin(), source.end());
- benchmark::DoNotOptimize(m);
- benchmark::ClobberMemory();
- }
-}
-
-void product_iterator_benchmark_zip_view(benchmark::State& state) {
- const std::size_t size = state.range(0);
-
- using M = std::flat_map<int, int>;
-
- const std::vector<int> keys = std::views::iota(0, static_cast<int>(size)) | std::ranges::to<std::vector<int>>();
- const std::vector<int> values = keys;
-
- auto source = std::views::zip(keys, values);
- for (auto _ : state) {
- M m;
- m.insert(std::sorted_unique, source.begin(), source.end());
- benchmark::DoNotOptimize(m);
- benchmark::ClobberMemory();
- }
-}
-
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::flat_map<int, int>>("std::flat_map<int, int>");
- benchmark::RegisterBenchmark("flat_map::insert_product_iterator_flat_map", product_iterator_benchmark_flat_map)
- ->Arg(32)
- ->Arg(1024)
- ->Arg(8192)
- ->Arg(65536);
- benchmark::RegisterBenchmark("flat_map::insert_product_iterator_zip", product_iterator_benchmark_zip_view)
- ->Arg(32)
- ->Arg(1024)
- ->Arg(8192)
- ->Arg(65536);
-
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
diff --git a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
new file mode 100644
index 0000000000000..6f2fa60fcf5a5
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+constexpr bool test() {
+ {
+ // Test that the __get_iterator_element can handle a non-copyable iterator
+ int Date[] = {1, 2, 3, 4};
+ cpp20_input_iterator<int*> iter(Date);
+ sentinel_wrapper<cpp20_input_iterator<int*>> sent{cpp20_input_iterator<int*>(Date + 4)};
+ std::ranges::subrange r1(std::move(iter), std::move(sent));
+ auto v = std::views::zip(std::move(r1), std::views::iota(0, 4));
+ auto it = v.begin();
+
+ using Iter = decltype(it);
+
+ static_assert(!std::is_copy_constructible_v<Iter>);
+
+ static_assert(std::__product_iterator_traits<Iter>::__size == 2);
+ std::same_as<cpp20_input_iterator<int*>&> decltype(auto) it1 =
+ std::__product_iterator_traits<Iter>::__get_iterator_element<0>(it);
+
+ assert(*it1 == 1);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
>From d7ccccbbaf64dde95cf3681a5cab2e6de8e66b59 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 18 May 2025 13:43:39 +0100
Subject: [PATCH 07/10] ci
---
.../containers/associative/associative_container_benchmarks.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 098e70102e343..01146939d278d 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -13,6 +13,7 @@
#include <iterator>
#include <random>
#include <string>
+#include <ranges>
#include <type_traits>
#include <utility>
#include <vector>
>From b9f40d02d11e4731e51c7b1efce3590e65ed49ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 18 May 2025 14:58:30 +0100
Subject: [PATCH 08/10] CI
---
.../containers/associative/associative_container_benchmarks.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 01146939d278d..39c35aaf94172 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -328,7 +328,6 @@ void associative_container_benchmarks(std::string container) {
bench("insert(iterator, iterator) (product_iterator from same type)", [=](auto& st) {
const std::size_t size = st.range(0);
std::vector<Value> in = make_value_types(generate_unique_keys(size + (size / 10)));
- std::sort(in.begin(), in.end(), [&](const auto& x, const auto& y) { return get_key(x) < get_key(y); });
Container source(in.begin(), in.end());
Container c;
>From 7c8e016c050b105c0e2ad01e336e45c25d7589b1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Jun 2025 16:01:52 +0100
Subject: [PATCH 09/10] review
---
libcxx/include/__iterator/product_iterator.h | 4 ++++
.../containers/associative/flat_map.bench.cpp | 1 -
.../libcxx/iterators/product_iterator.pass.cpp | 17 +++++++++++++++++
3 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/__iterator/product_iterator.h b/libcxx/include/__iterator/product_iterator.h
index 11f340621e128..0d54137104fb0 100644
--- a/libcxx/include/__iterator/product_iterator.h
+++ b/libcxx/include/__iterator/product_iterator.h
@@ -23,6 +23,10 @@
// - template <size_t _N>
// static decltype(auto) Traits::__get_iterator_element(It&& __it)
// Returns the _Nth iterator element of the given product iterator.
+//
+// - template <class... _Iters>
+// static _Iterator __make_product_iterator(_Iters&&...);
+// Creates a product iterator from the given underlying iterators.
#include <__config>
#include <__cstddef/size_t.h>
diff --git a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
index 034602879be1b..82902d50f31e6 100644
--- a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
@@ -10,7 +10,6 @@
#include <flat_map>
#include <utility>
-#include <ranges>
#include "associative_container_benchmarks.h"
#include "../../GenerateInput.h"
diff --git a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
index 6f2fa60fcf5a5..038790238ae5c 100644
--- a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
+++ b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
@@ -8,8 +8,10 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17
+#include <flat_map>
#include <ranges>
#include <type_traits>
+#include <utility>
#include "test_macros.h"
#include "test_iterators.h"
@@ -34,6 +36,21 @@ constexpr bool test() {
assert(*it1 == 1);
}
+ if (!std::is_constant_evaluated()) {
+ // Test __make_product_iterator
+ using M = std::flat_map<int, int>;
+ M m{{1, 1}, {2, 2}, {3, 3}};
+ using Iter = std::ranges::iterator_t<const M>;
+ const auto& keys = m.keys();
+ const auto& values = m.values();
+
+ auto it_keys = std::ranges::begin(keys);
+ auto it_values = std::ranges::begin(values);
+
+ auto it = std::__product_iterator_traits<Iter>::__make_product_iterator(it_keys, it_values);
+ assert(it->first == 1);
+ assert(it->second == 1);
+ }
return true;
}
>From 61f498a2fc4b4569a887b1368918d8597485f2b8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 7 Jun 2025 16:38:36 +0100
Subject: [PATCH 10/10] CI
---
libcxx/test/libcxx/iterators/product_iterator.pass.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
index 038790238ae5c..6a645b7b5c5db 100644
--- a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
+++ b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp
@@ -12,6 +12,7 @@
#include <ranges>
#include <type_traits>
#include <utility>
+#include <vector>
#include "test_macros.h"
#include "test_iterators.h"
More information about the libcxx-commits
mailing list