From 757d24e86ab3bcea2a9cab822989e19bf1ba4b13 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 11 May 2025 16:39:34 +0100 Subject: [PATCH 01/15] [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 e386f31386b60..c334b25574305 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 f163dfc28706d..d1e0b92a79bb5 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 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 @@ -181,6 +184,23 @@ struct __key_value_iterator { } }; +template +struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const>> { + static constexpr size_t __size = 2; + + template + _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 27687ae8de3bc..51df634d78764 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 + _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 +// 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 +struct __product_iterator_traits; +/* exposition-only: +{ + static constexpr size_t __size = ...; + + template + static auto __get_iterator_element(_Iterator); +}; +*/ + +template +struct __is_product_iterator : false_type {}; + +template +struct __is_product_iterator<_Tp, sizeof(__product_iterator_traits<_Tp>) * 0> : true_type {}; + +template +struct __is_product_iterator_of_size : false_type {}; + +template +struct __is_product_iterator_of_size<_Tp, _Size, __enable_if_t<__product_iterator_traits<_Tp>::__size == _Size>> + : true_type {}; + +template +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>...>; @@ -468,6 +473,18 @@ inline constexpr auto zip = __zip::__fn{}; } // namespace views } // namespace ranges +template + requires _Iter::__is_zip_view_iterator::value +struct __product_iterator_traits<_Iter> { + static constexpr size_t __size = tuple_size().__current_)>::value; + + template + 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 #include +#include #include "associative_container_benchmarks.h" #include "../../GenerateInput.h" @@ -26,9 +27,54 @@ struct support::adapt_operations> { 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; + + const M source = + std::views::iota(0, static_cast(size)) | std::views::transform([](int i) { return std::pair(i, i); }) | + std::ranges::to>(); + + 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; + + const std::vector keys = std::views::iota(0, static_cast(size)) | std::ranges::to>(); + const std::vector 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"); + 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 66bd4022f321a..9bcdc9c1f2b9e 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 #include #include +#include #include "MinSequenceContainer.h" #include "../helpers.h" @@ -95,6 +96,26 @@ constexpr void test() { }); } +constexpr void test_product_iterator() { + using M = std::flat_map; + { + 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 keys{1, 2, 3}; + std::vector 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); + } +} + constexpr bool test() { test, std::vector>(); #ifndef __cpp_lib_constexpr_deque @@ -105,7 +126,7 @@ constexpr bool test() { } test, MinSequenceContainer>(); test>, std::vector>>(); - + test_product_iterator(); if (!TEST_IS_CONSTANT_EVALUATED) { auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); }; test_insert_range_exception_guarantee(insert_func); From e47e2229b746282288b880e0488b0efe9b35c8fb Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 11 May 2025 19:08:03 +0100 Subject: [PATCH 02/15] 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 d1e0b92a79bb5..b95c867fcd61e 100644 --- a/libcxx/include/__flat_map/key_value_iterator.h +++ b/libcxx/include/__flat_map/key_value_iterator.h @@ -188,12 +188,12 @@ template > { static constexpr size_t __size = 2; - template + template _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 struct __is_product_iterator_of_size : false_type {}; template -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 -using __product_iterator_element_t = - decltype(__product_iterator_traits<_Iterator>::__get_iterator_element<_N>(declval<_Iterator>())); +template +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 struct __product_iterator_traits<_Iter> { static constexpr size_t __size = tuple_size().__current_)>::value; - template - requires(_N < __size) + template + 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 860d9bc93b9bafb3eef8b8742f6938970d541907 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 11 May 2025 19:20:07 +0100 Subject: [PATCH 03/15] 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 adf80f2ac9acb..f878e15d70b1a 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1522,6 +1522,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 921ace8ca860017ce7816552641ff348475cfc96 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 11 May 2025 19:43:57 +0100 Subject: [PATCH 04/15] 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 b95c867fcd61e..8decad074368b 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 1ec6d722a55ee7270565dd3727d3da28732a9e7a Mon Sep 17 00:00:00 2001 From: Hui Date: Sat, 17 May 2025 16:46:39 +0100 Subject: [PATCH 05/15] Apply suggestions from code review Co-authored-by: Louis Dionne --- 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 78c0fdb3d105e0943f05dfbd28392234c9b4fb44 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 18 May 2025 11:27:24 +0100 Subject: [PATCH 06/15] 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 8decad074368b..3f08254bef6f1 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> @@ -189,17 +190,22 @@ template > { static constexpr size_t __size = 2; - template - _LIBCPP_HIDE_FROM_ABI static auto - __get_iterator_element(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, _Const> __it) + template + _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 + _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 51df634d78764..e9ee1884567e0 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 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 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 -// 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 - static auto __get_iterator_element(_Iterator); + template + static decltype(auto) __get_iterator_element(_Iter&&); + + template + 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 - requires _Iter::__is_zip_view_iterator::value -struct __product_iterator_traits<_Iter> { - static constexpr size_t __size = tuple_size().__current_)>::value; +template + requires _Iterator::__is_zip_view_iterator +struct __product_iterator_traits<_Iterator> { + static constexpr size_t __size = tuple_size().__current_)>::value; - template + template 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 + _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 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 keys = generate_unique_keys(size + (size / 10)); + std::sort(keys.begin(), keys.end()); + std::vector 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> { 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; - - const M source = - std::views::iota(0, static_cast(size)) | std::views::transform([](int i) { return std::pair(i, i); }) | - std::ranges::to>(); - - 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; - - const std::vector keys = std::views::iota(0, static_cast(size)) | std::ranges::to>(); - const std::vector 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"); - 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 +#include + +#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 iter(Date); + sentinel_wrapper> sent{cpp20_input_iterator(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); + + static_assert(std::__product_iterator_traits::__size == 2); + std::same_as&> decltype(auto) it1 = + std::__product_iterator_traits::__get_iterator_element<0>(it); + + assert(*it1 == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} From f8e545f6068dabbee17d8a263c48c7563b0ea2a4 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 18 May 2025 13:43:39 +0100 Subject: [PATCH 07/15] 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 #include #include +#include #include #include #include From f7bf9e8edb06903acf4d7e585d44458877f32ad8 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 18 May 2025 14:58:30 +0100 Subject: [PATCH 08/15] 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 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 57afa882339137e2b2023b413ed642a7e382cded Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sat, 7 Jun 2025 16:01:52 +0100 Subject: [PATCH 09/15] 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 // static decltype(auto) Traits::__get_iterator_element(It&& __it) // Returns the _Nth iterator element of the given product iterator. +// +// - template +// 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 #include -#include #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 #include #include +#include #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; + M m{{1, 1}, {2, 2}, {3, 3}}; + using Iter = std::ranges::iterator_t; + 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::__make_product_iterator(it_keys, it_values); + assert(it->first == 1); + assert(it->second == 1); + } return true; } From 7314fe0415998367d600458f21dfd1810eb045dc Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sat, 7 Jun 2025 16:38:36 +0100 Subject: [PATCH 10/15] 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 #include #include +#include #include "test_macros.h" #include "test_iterators.h" From 038de7dbd4e70747c885eebbd84548a5a4ce3c72 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 8 Jun 2025 09:31:22 +0100 Subject: [PATCH 11/15] CI --- libcxx/include/__iterator/product_iterator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/__iterator/product_iterator.h b/libcxx/include/__iterator/product_iterator.h index 0d54137104fb0..1db673bafd337 100644 --- a/libcxx/include/__iterator/product_iterator.h +++ b/libcxx/include/__iterator/product_iterator.h @@ -69,7 +69,7 @@ struct __is_product_iterator_of_size<_Tp, _Size, __enable_if_t<__product_iterato template using __product_iterator_element_t _LIBCPP_NODEBUG = - decltype(__product_iterator_traits<_Iterator>::__get_iterator_element<_Nth>(std::declval<_Iterator>())); + decltype(__product_iterator_traits<_Iterator>::template __get_iterator_element<_Nth>(std::declval<_Iterator>())); _LIBCPP_END_NAMESPACE_STD From b39029cc83b216326c9e1d56ae973c80fcc141e2 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sat, 14 Jun 2025 20:54:19 +0100 Subject: [PATCH 12/15] release notes and gcc 15 filter out --- libcxx/docs/ReleaseNotes/21.rst | 3 +++ libcxx/test/libcxx/iterators/product_iterator.pass.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index c52bc54f412bd..08b32bb508dc1 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -81,6 +81,9 @@ Improvements and New Features - The ``bitset::to_string`` function has been optimized, resulting in a performance improvement of up to 8.3x for bitsets with uniformly distributed zeros and ones, and up to 13.5x and 16.1x for sparse and dense bitsets, respectively. +- The ``flat_map::insert`` and ``flat_set::insert_range`` have been optimized, resulting in a performance improvement of up + to 10x for inserting elements into a ``flat_map`` when the input range is a ``flat_map`` or a ``zip_view``. + Deprecations and Removals ------------------------- diff --git a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp index 6a645b7b5c5db..e35a1a3dde3d7 100644 --- a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp +++ b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 +// gcc 15 does not seem to recognize the __product_iterator_traits specializations +// UNSUPPORTED: gcc #include #include From f6e641868f88deb34fad13cab0f92cc2ffc52c0e Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 15 Jun 2025 21:35:39 +0100 Subject: [PATCH 13/15] ci --- .../containers/associative/associative_container_benchmarks.h | 3 +++ .../test/benchmarks/containers/associative/flat_map.bench.cpp | 2 +- .../benchmarks/containers/associative/flat_multimap.bench.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h index 39c35aaf94172..0e65f44fd483e 100644 --- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h +++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h @@ -20,6 +20,7 @@ #include "benchmark/benchmark.h" #include "../../GenerateInput.h" +#include "test_macros.h" namespace support { @@ -343,6 +344,7 @@ void associative_container_benchmarks(std::string container) { } }); +#if TEST_STD_VER >= 23 bench("insert(iterator, iterator) (product_iterator from zip_view)", [=](auto& st) { const std::size_t size = st.range(0); std::vector keys = generate_unique_keys(size + (size / 10)); @@ -363,6 +365,7 @@ void associative_container_benchmarks(std::string container) { st.ResumeTiming(); } }); +#endif } ///////////////////////// // Erasure diff --git a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp index 82902d50f31e6..f3b86554802ca 100644 --- a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp +++ b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// REQUIRES: std-at-least-c++26 +// REQUIRES: std-at-least-c++23 #include #include diff --git a/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp index f752f79b3b454..80eaa549042c6 100644 --- a/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp +++ b/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// REQUIRES: std-at-least-c++26 +// REQUIRES: std-at-least-c++23 #include From 01bfe42354141e290b9d60f37d5a46766a9b0d3d Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sat, 21 Jun 2025 20:23:38 +0100 Subject: [PATCH 14/15] rebase --- libcxx/include/__flat_map/utils.h | 2 +- libcxx/test/libcxx/iterators/product_iterator.pass.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/__flat_map/utils.h b/libcxx/include/__flat_map/utils.h index e9ee1884567e0..3a05c715660dc 100644 --- a/libcxx/include/__flat_map/utils.h +++ b/libcxx/include/__flat_map/utils.h @@ -94,7 +94,7 @@ struct __flat_map_utils { } template - _LIBCPP_HIDE_FROM_ABI static typename _Map::size_type + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static typename _Map::size_type __append(_Map& __map, _InputIterator __first, _InputIterator __last) requires __is_product_iterator_of_size<_InputIterator, 2>::value { diff --git a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp index e35a1a3dde3d7..604f3667c36e3 100644 --- a/libcxx/test/libcxx/iterators/product_iterator.pass.cpp +++ b/libcxx/test/libcxx/iterators/product_iterator.pass.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // gcc 15 does not seem to recognize the __product_iterator_traits specializations // UNSUPPORTED: gcc From 6b57b90ef6ccdad195989fc7b7ecf6adf12928aa Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sat, 21 Jun 2025 20:47:40 +0100 Subject: [PATCH 15/15] constexpr fix --- libcxx/include/__flat_map/key_value_iterator.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libcxx/include/__flat_map/key_value_iterator.h b/libcxx/include/__flat_map/key_value_iterator.h index 3f08254bef6f1..d04a23d1f8606 100644 --- a/libcxx/include/__flat_map/key_value_iterator.h +++ b/libcxx/include/__flat_map/key_value_iterator.h @@ -191,7 +191,7 @@ struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _Ma static constexpr size_t __size = 2; template - _LIBCPP_HIDE_FROM_ABI static decltype(auto) __get_iterator_element(_Iter&& __it) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static decltype(auto) __get_iterator_element(_Iter&& __it) requires(_Nth <= 1) { if constexpr (_Nth == 0) { @@ -202,7 +202,8 @@ struct __product_iterator_traits<__key_value_iterator<_Owner, _KeyContainer, _Ma } template - _LIBCPP_HIDE_FROM_ABI static auto __make_product_iterator(_KeyIter&& __key_iter, _MappedIter&& __mapped_iter) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 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)); }