[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 08:38:51 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