[libcxx-commits] [libcxx] [libc++] Speed up vector<bool> copy/move-ctors [1/3] (PR #120132)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 21 07:47:07 PST 2025


https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/120132

>From 0d951615fc0e9d12ada2fa9238b21782232c3d3c Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Mon, 16 Dec 2024 13:11:30 -0500
Subject: [PATCH 1/2] Speed-up copy/move-ctors for vector<bool>

---
 libcxx/include/__vector/vector_bool.h         | 28 +++++++++------
 .../containers/ContainerBenchmarks.h          | 30 ++++++++++++++++
 .../vector_bool_operations.bench.cpp          | 36 +++++++++++++++++++
 3 files changed, 83 insertions(+), 11 deletions(-)
 create mode 100644 libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp

diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 2b721e00058bc6..95cc773479d4b2 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -417,6 +417,8 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
     __guard.__complete();
   }
 
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __alloc_and_copy(const vector& __v);
+
   template <class _Iterator, class _Sentinel>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last);
 
@@ -692,25 +694,30 @@ vector<bool, _Allocator>::vector(initializer_list<value_type> __il, const alloca
 
 #endif // _LIBCPP_CXX03_LANG
 
+// This function copies each storage word as a whole, which is substantially more efficient than copying
+// individual bits within each word
+template <class _Allocator>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void vector<bool, _Allocator>::__alloc_and_copy(const vector& __v) {
+  if (__v.__size_) {
+    __vallocate(__v.__size_);
+    std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.__size_), __begin_);
+  }
+  __size_ = __v.__size_;
+}
+
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const vector& __v)
     : __begin_(nullptr),
       __size_(0),
       __cap_(0),
       __alloc_(__storage_traits::select_on_container_copy_construction(__v.__alloc_)) {
-  if (__v.size() > 0) {
-    __vallocate(__v.size());
-    __construct_at_end(__v.begin(), __v.end(), __v.size());
-  }
+  __alloc_and_copy(__v);
 }
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const vector& __v, const allocator_type& __a)
     : __begin_(nullptr), __size_(0), __cap_(0), __alloc_(__a) {
-  if (__v.size() > 0) {
-    __vallocate(__v.size());
-    __construct_at_end(__v.begin(), __v.end(), __v.size());
-  }
+  __alloc_and_copy(__v);
 }
 
 template <class _Allocator>
@@ -755,9 +762,8 @@ vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator
     this->__cap_   = __v.__cap_;
     __v.__begin_   = nullptr;
     __v.__cap_ = __v.__size_ = 0;
-  } else if (__v.size() > 0) {
-    __vallocate(__v.size());
-    __construct_at_end(__v.begin(), __v.end(), __v.size());
+  } else {
+    __alloc_and_copy(__v);
   }
 }
 
diff --git a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
index 5fc8981619672c..a9ae36e43da8b9 100644
--- a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
+++ b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
@@ -39,6 +39,36 @@ void BM_CopyConstruct(benchmark::State& st, Container) {
   }
 }
 
+template <class Container>
+void BM_MoveConstruct(benchmark::State& st, Container) {
+  auto size = st.range(0);
+  Container c(size);
+  for (auto _ : st) {
+    auto v = std::move(c);
+    DoNotOptimizeData(v);
+  }
+}
+
+template <class Container, class Allocator>
+void BM_CopyConstruct_Alloc(benchmark::State& st, Container, Allocator a) {
+  auto size = st.range(0);
+  Container c(size);
+  for (auto _ : st) {
+    Container v(c, a);
+    DoNotOptimizeData(v);
+  }
+}
+
+template <class Container, class Allocator>
+void BM_MoveConstruct_Alloc(benchmark::State& st, Container, Allocator a) {
+  auto size = st.range(0);
+  Container c(size);
+  for (auto _ : st) {
+    Container v(std::move(c), a);
+    DoNotOptimizeData(v);
+  }
+}
+
 template <class Container>
 void BM_Assignment(benchmark::State& st, Container) {
   auto size = st.range(0);
diff --git a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
new file mode 100644
index 00000000000000..7de6b344d43943
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "ContainerBenchmarks.h"
+#include "../GenerateInput.h"
+#include "test_allocator.h"
+
+using namespace ContainerBenchmarks;
+
+BENCHMARK_CAPTURE(BM_CopyConstruct, vector_bool, std::vector<bool>{})->Arg(5140480);
+BENCHMARK_CAPTURE(BM_MoveConstruct, vector_bool, std::vector<bool>{})->Arg(5140480);
+BENCHMARK_CAPTURE(
+    BM_CopyConstruct_Alloc, vector_bool, std::vector<bool, test_allocator<bool>>(), test_allocator<bool>(3))
+    ->Arg(5140480);
+BENCHMARK_CAPTURE(
+    BM_MoveConstruct_Alloc, vector_bool, std::vector<bool, test_allocator<bool>>(), test_allocator<bool>(3))
+    ->Arg(5140480);
+
+BENCHMARK_MAIN();
\ No newline at end of file

>From f59815386daa48705a56b6024837f499cdbea331 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Tue, 21 Jan 2025 10:46:07 -0500
Subject: [PATCH 2/2] Inline call to std::copy and enhance test coverage

---
 libcxx/include/__vector/vector_bool.h         |  31 ++--
 .../sequences/vector.bool/copy.pass.cpp       | 112 ++++++------
 .../sequences/vector.bool/copy_alloc.pass.cpp | 108 ++++++-----
 .../sequences/vector.bool/move.pass.cpp       | 169 ++++++++++--------
 .../sequences/vector.bool/move_alloc.pass.cpp | 142 ++++++++-------
 5 files changed, 302 insertions(+), 260 deletions(-)

diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 95cc773479d4b2..c7b6617ad723ca 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -417,8 +417,6 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
     __guard.__complete();
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __alloc_and_copy(const vector& __v);
-
   template <class _Iterator, class _Sentinel>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last);
 
@@ -694,30 +692,27 @@ vector<bool, _Allocator>::vector(initializer_list<value_type> __il, const alloca
 
 #endif // _LIBCPP_CXX03_LANG
 
-// This function copies each storage word as a whole, which is substantially more efficient than copying
-// individual bits within each word
-template <class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void vector<bool, _Allocator>::__alloc_and_copy(const vector& __v) {
-  if (__v.__size_) {
-    __vallocate(__v.__size_);
-    std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.__size_), __begin_);
-  }
-  __size_ = __v.__size_;
-}
-
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const vector& __v)
     : __begin_(nullptr),
       __size_(0),
       __cap_(0),
       __alloc_(__storage_traits::select_on_container_copy_construction(__v.__alloc_)) {
-  __alloc_and_copy(__v);
+  if (__v.size() > 0) {
+    __vallocate(__v.size());
+    std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.size()), __begin_);
+    __size_ = __v.size();
+  }
 }
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const vector& __v, const allocator_type& __a)
     : __begin_(nullptr), __size_(0), __cap_(0), __alloc_(__a) {
-  __alloc_and_copy(__v);
+  if (__v.size() > 0) {
+    __vallocate(__v.size());
+    std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.size()), __begin_);
+    __size_ = __v.size();
+  }
 }
 
 template <class _Allocator>
@@ -762,8 +757,10 @@ vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator
     this->__cap_   = __v.__cap_;
     __v.__begin_   = nullptr;
     __v.__cap_ = __v.__size_ = 0;
-  } else {
-    __alloc_and_copy(__v);
+  } else if (__v.size() > 0) {
+    __vallocate(__v.size());
+    std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.size()), __begin_);
+    __size_ = __v.size();
   }
 }
 
diff --git a/libcxx/test/std/containers/sequences/vector.bool/copy.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/copy.pass.cpp
index bfd37e956dbdf0..f9837de94f8829 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/copy.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/copy.pass.cpp
@@ -11,64 +11,76 @@
 
 // vector(const vector& v);
 
-#include <vector>
 #include <cassert>
+#include <memory>
+#include <vector>
 
-#include "test_macros.h"
-#include "test_allocator.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
 
-template <class C>
-TEST_CONSTEXPR_CXX20 void test(const C& x)
-{
-    typename C::size_type s = x.size();
-    C c(x);
-    LIBCPP_ASSERT(c.__invariants());
-    assert(c.size() == s);
-    assert(c == x);
-}
-
-TEST_CONSTEXPR_CXX20 bool tests()
-{
-    {
-        bool a[] = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
-        bool* an = a + sizeof(a)/sizeof(a[0]);
-        test(std::vector<bool>(a, an));
-    }
-    {
-        std::vector<bool, test_allocator<bool> > v(3, true, test_allocator<bool>(5));
-        std::vector<bool, test_allocator<bool> > v2 = v;
-        assert(v2 == v);
-        assert(v2.get_allocator() == v.get_allocator());
-    }
+template <class A>
+TEST_CONSTEXPR_CXX20 void test(const std::vector<bool, A>& x) {
+  std::vector<bool, A> c(x);
+  LIBCPP_ASSERT(c.__invariants());
+  assert(c.size() == x.size());
+  assert(c == x);
 #if TEST_STD_VER >= 11
-    {
-        std::vector<bool, other_allocator<bool> > v(3, true, other_allocator<bool>(5));
-        std::vector<bool, other_allocator<bool> > v2 = v;
-        assert(v2 == v);
-        assert(v2.get_allocator() == other_allocator<bool>(-2));
-    }
-    {
-        bool a[] = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
-        bool* an = a + sizeof(a)/sizeof(a[0]);
-        test(std::vector<bool, min_allocator<bool>>(a, an));
-    }
-    {
-        std::vector<bool, min_allocator<bool> > v(3, true, min_allocator<bool>());
-        std::vector<bool, min_allocator<bool> > v2 = v;
-        assert(v2 == v);
-        assert(v2.get_allocator() == v.get_allocator());
-    }
+  assert(c.get_allocator() == std::allocator_traits<A>::select_on_container_copy_construction(x.get_allocator()));
 #endif
+}
+
+TEST_CONSTEXPR_CXX20 bool tests() {
+  bool a05[5]  = {1, 0, 1, 0, 1};
+  bool a17[17] = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1};
+  bool a33[33] = {1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1};
+  bool a65[65] = {0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0};
+
+  { // Test the copy constructor with the default allocator
+    test(std::vector<bool>(a05, a05 + sizeof(a05) / sizeof(a05[0])));
+    test(std::vector<bool>(a17, a17 + sizeof(a17) / sizeof(a17[0])));
+    test(std::vector<bool>(a33, a33 + sizeof(a33) / sizeof(a33[0])));
+    test(std::vector<bool>(a65, a65 + sizeof(a65) / sizeof(a65[0])));
+    test(std::vector<bool>(257, true));
+  }
+
+  { // Test the copy constructor with test_allocator
+    using A = test_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a05, a05 + sizeof(a05) / sizeof(a05[0]), A(5)));
+    test(C(a17, a17 + sizeof(a17) / sizeof(a17[0]), A(5)));
+    test(C(a33, a33 + sizeof(a33) / sizeof(a33[0]), A(5)));
+    test(C(a65, a65 + sizeof(a65) / sizeof(a65[0]), A(5)));
+    test(C(257, true, A(5)));
+  }
+
+  { // Test the copy constructor with other_allocator
+    using A = other_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a05, a05 + sizeof(a05) / sizeof(a05[0]), A(5)));
+    test(C(a17, a17 + sizeof(a17) / sizeof(a17[0]), A(5)));
+    test(C(a33, a33 + sizeof(a33) / sizeof(a33[0]), A(5)));
+    test(C(a65, a65 + sizeof(a65) / sizeof(a65[0]), A(5)));
+    test(C(257, true, A(5)));
+  }
+
+  { // Test the copy constructor with min_allocator
+    using A = min_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a05, a05 + sizeof(a05) / sizeof(a05[0]), A()));
+    test(C(a17, a17 + sizeof(a17) / sizeof(a17[0]), A()));
+    test(C(a33, a33 + sizeof(a33) / sizeof(a33[0]), A()));
+    test(C(a65, a65 + sizeof(a65) / sizeof(a65[0]), A()));
+    test(C(257, true, A()));
+  }
 
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
-    tests();
-#if TEST_STD_VER > 17
-    static_assert(tests());
+int main(int, char**) {
+  tests();
+#if TEST_STD_VER >= 20
+  static_assert(tests());
 #endif
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/vector.bool/copy_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/copy_alloc.pass.cpp
index 468bf5d866004e..0565f52f6e075b 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/copy_alloc.pass.cpp
@@ -7,67 +7,77 @@
 //===----------------------------------------------------------------------===//
 
 // <vector>
+// vector<bool>
 
 // vector(const vector& v, const allocator_type& a);
 
-#include <vector>
 #include <cassert>
+#include <vector>
 
-#include "test_macros.h"
-#include "test_allocator.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
 
-template <class C>
-TEST_CONSTEXPR_CXX20 void test(const C& x, const typename C::allocator_type& a)
-{
-    typename C::size_type s = x.size();
-    C c(x, a);
-    LIBCPP_ASSERT(c.__invariants());
-    assert(c.size() == s);
-    assert(c == x);
+template <class A>
+TEST_CONSTEXPR_CXX20 void test(const std::vector<bool, A>& x, const A& a) {
+  std::vector<bool, A> c(x, a);
+  LIBCPP_ASSERT(c.__invariants());
+  assert(c.size() == x.size());
+  assert(c == x);
+  assert(c.get_allocator() == a);
 }
 
-TEST_CONSTEXPR_CXX20 bool tests()
-{
-    {
-        bool a[] = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
-        bool* an = a + sizeof(a)/sizeof(a[0]);
-        test(std::vector<bool>(a, an), std::allocator<bool>());
-    }
-    {
-        std::vector<bool, test_allocator<bool> > l(3, true, test_allocator<bool>(5));
-        std::vector<bool, test_allocator<bool> > l2(l, test_allocator<bool>(3));
-        assert(l2 == l);
-        assert(l2.get_allocator() == test_allocator<bool>(3));
-    }
-    {
-        std::vector<bool, other_allocator<bool> > l(3, true, other_allocator<bool>(5));
-        std::vector<bool, other_allocator<bool> > l2(l, other_allocator<bool>(3));
-        assert(l2 == l);
-        assert(l2.get_allocator() == other_allocator<bool>(3));
-    }
-#if TEST_STD_VER >= 11
-    {
-        bool a[] = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
-        bool* an = a + sizeof(a)/sizeof(a[0]);
-        test(std::vector<bool, min_allocator<bool>>(a, an), min_allocator<bool>());
-    }
-    {
-        std::vector<bool, min_allocator<bool> > l(3, true, min_allocator<bool>());
-        std::vector<bool, min_allocator<bool> > l2(l, min_allocator<bool>());
-        assert(l2 == l);
-        assert(l2.get_allocator() == min_allocator<bool>());
-    }
-#endif
+TEST_CONSTEXPR_CXX20 bool tests() {
+  bool a05[5]  = {1, 0, 1, 0, 1};
+  bool a17[17] = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1};
+  bool a33[33] = {1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1};
+  bool a65[65] = {0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0};
+
+  { // Test with the default allocator
+    test(std::vector<bool>(a05, a05 + sizeof(a05) / sizeof(a05[0])), std::allocator<bool>());
+    test(std::vector<bool>(a17, a17 + sizeof(a17) / sizeof(a17[0])), std::allocator<bool>());
+    test(std::vector<bool>(a33, a33 + sizeof(a33) / sizeof(a33[0])), std::allocator<bool>());
+    test(std::vector<bool>(a65, a65 + sizeof(a65) / sizeof(a65[0])), std::allocator<bool>());
+    test(std::vector<bool>(257, true), std::allocator<bool>());
+  }
+
+  { // Test with test_allocator
+    using A = test_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a05, a05 + sizeof(a05) / sizeof(a05[0]), A(5)), A(3));
+    test(C(a17, a17 + sizeof(a17) / sizeof(a17[0]), A(5)), A(3));
+    test(C(a33, a33 + sizeof(a33) / sizeof(a33[0]), A(5)), A(3));
+    test(C(a65, a65 + sizeof(a65) / sizeof(a65[0]), A(5)), A(3));
+    test(C(257, true, A(5)), A(3));
+  }
+
+  { // Test with other_allocator
+    using A = other_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a05, a05 + sizeof(a05) / sizeof(a05[0]), A(5)), A(3));
+    test(C(a17, a17 + sizeof(a17) / sizeof(a17[0]), A(5)), A(3));
+    test(C(a33, a33 + sizeof(a33) / sizeof(a33[0]), A(5)), A(3));
+    test(C(a65, a65 + sizeof(a65) / sizeof(a65[0]), A(5)), A(3));
+    test(C(257, true, A(5)), A(3));
+  }
+
+  { // Test with min_allocator
+    using A = min_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a05, a05 + sizeof(a05) / sizeof(a05[0]), A()), A());
+    test(C(a17, a17 + sizeof(a17) / sizeof(a17[0]), A()), A());
+    test(C(a33, a33 + sizeof(a33) / sizeof(a33[0]), A()), A());
+    test(C(a65, a65 + sizeof(a65) / sizeof(a65[0]), A()), A());
+    test(C(257, true, A()), A());
+  }
 
   return true;
 }
 
-int main(int, char**)
-{
-    tests();
-#if TEST_STD_VER > 17
-    static_assert(tests());
+int main(int, char**) {
+  tests();
+#if TEST_STD_VER >= 20
+  static_assert(tests());
 #endif
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/vector.bool/move.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/move.pass.cpp
index 56de3372258151..b8dcd1e39b975c 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/move.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/move.pass.cpp
@@ -9,97 +9,108 @@
 // UNSUPPORTED: c++03
 
 // <vector>
+// vector<bool>
 
 // vector(vector&& c);
 
-#include <vector>
 #include <cassert>
-#include "test_macros.h"
-#include "test_allocator.h"
+#include <vector>
+
 #include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
 
-TEST_CONSTEXPR_CXX20 bool tests()
-{
-    test_allocator_statistics alloc_stats;
-    {
-        std::vector<bool, test_allocator<bool> > l(test_allocator<bool>(5, &alloc_stats));
-        std::vector<bool, test_allocator<bool> > lo(test_allocator<bool>(5, &alloc_stats));
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(true);
-            lo.push_back(true);
-        }
-        std::vector<bool, test_allocator<bool> > l2 = std::move(l);
-        assert(l2 == lo);
-        assert(l.empty());
-        assert(l2.get_allocator() == lo.get_allocator());
-    }
-    {
-        std::vector<bool, other_allocator<bool> > l(other_allocator<bool>(5));
-        std::vector<bool, other_allocator<bool> > lo(other_allocator<bool>(5));
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(true);
-            lo.push_back(true);
-        }
-        std::vector<bool, other_allocator<bool> > l2 = std::move(l);
-        assert(l2 == lo);
-        assert(l.empty());
-        assert(l2.get_allocator() == lo.get_allocator());
-    }
+template <unsigned N, class A>
+TEST_CONSTEXPR_CXX20 void test(const A& a) {
+  std::vector<bool, A> l(N, true, a);
+  std::vector<bool, A> lo(N, true, a);
+  for (unsigned i = 1; i < N; i += 2) {
+    l[i]  = false;
+    lo[i] = false;
+  }
+  std::vector<bool, A> l2 = std::move(l);
+  assert(l2 == lo);
+  assert(l.empty());
+  assert(l2.get_allocator() == lo.get_allocator());
+}
+
+TEST_CONSTEXPR_CXX20 bool tests() {
+  test_allocator_statistics alloc_stats;
+
+  { // Test move constructor with default allocator
+    using A = std::allocator<bool>;
+    test<5>(A());
+    test<17>(A());
+    test<33>(A());
+    test<65>(A());
+    test<257>(A());
+  }
+
+  { // Test move constructor with test_allocator
+    using A = test_allocator<bool>;
+    test<5>(A(5, &alloc_stats));
+    test<17>(A(5, &alloc_stats));
+    test<33>(A(5, &alloc_stats));
+    test<65>(A(5, &alloc_stats));
+    test<257>(A(5, &alloc_stats));
+  }
+
+  { // Test move constructor with other_allocator
+    using A = other_allocator<bool>;
+    test<5>(A(5));
+    test<17>(A(5));
+    test<33>(A(5));
+    test<65>(A(5));
+    test<257>(A(5));
+  }
+
+  { // Test move constructor with min_allocator
+    using A = min_allocator<bool>;
+    test<5>(A());
+    test<17>(A());
+    test<33>(A());
+    test<65>(A());
+    test<257>(A());
+  }
+
+  { // Test to verify the allocator statistics
+    alloc_stats.clear();
+    using Vect   = std::vector<bool, test_allocator<bool> >;
+    using AllocT = Vect::allocator_type;
+    Vect v(test_allocator<bool>(42, 101, &alloc_stats));
+    assert(alloc_stats.count == 1);
     {
-        std::vector<bool, min_allocator<bool> > l(min_allocator<bool>{});
-        std::vector<bool, min_allocator<bool> > lo(min_allocator<bool>{});
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(true);
-            lo.push_back(true);
-        }
-        std::vector<bool, min_allocator<bool> > l2 = std::move(l);
-        assert(l2 == lo);
-        assert(l.empty());
-        assert(l2.get_allocator() == lo.get_allocator());
+      const AllocT& a = v.get_allocator();
+      assert(alloc_stats.count == 2);
+      assert(a.get_data() == 42);
+      assert(a.get_id() == 101);
     }
+    assert(alloc_stats.count == 1);
+    alloc_stats.clear_ctor_counters();
+
+    Vect v2 = std::move(v);
+    assert(alloc_stats.count == 2);
+    assert(alloc_stats.copied == 0);
+    assert(alloc_stats.moved == 1);
     {
-      alloc_stats.clear();
-      using Vect = std::vector<bool, test_allocator<bool> >;
-      using AllocT = Vect::allocator_type;
-      Vect v(test_allocator<bool>(42, 101, &alloc_stats));
-      assert(alloc_stats.count == 1);
-      {
-        const AllocT& a = v.get_allocator();
-        assert(alloc_stats.count == 2);
-        assert(a.get_data() == 42);
-        assert(a.get_id() == 101);
-      }
-      assert(alloc_stats.count == 1);
-      alloc_stats.clear_ctor_counters();
-
-      Vect v2 = std::move(v);
-      assert(alloc_stats.count == 2);
-      assert(alloc_stats.copied == 0);
-      assert(alloc_stats.moved == 1);
-      {
-        const AllocT& a1 = v.get_allocator();
-        assert(a1.get_id() == test_alloc_base::moved_value);
-        assert(a1.get_data() == 42);
-
-        const AllocT& a2 = v2.get_allocator();
-        assert(a2.get_id() == 101);
-        assert(a2.get_data() == 42);
-
-        assert(a1 == a2);
-      }
+      const AllocT& a1 = v.get_allocator();
+      assert(a1.get_id() == test_alloc_base::moved_value);
+      assert(a1.get_data() == 42);
+
+      const AllocT& a2 = v2.get_allocator();
+      assert(a2.get_id() == 101);
+      assert(a2.get_data() == 42);
+      assert(a1 == a2);
     }
+  }
 
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
-    tests();
-#if TEST_STD_VER > 17
-    static_assert(tests());
+int main(int, char**) {
+  tests();
+#if TEST_STD_VER >= 20
+  static_assert(tests());
 #endif
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/vector.bool/move_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/move_alloc.pass.cpp
index 1f41657eb56eac..0bb997021bcc74 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/move_alloc.pass.cpp
@@ -9,78 +9,90 @@
 // UNSUPPORTED: c++03
 
 // <vector>
+// vector<bool>
 
 // vector(vector&& c, const allocator_type& a);
 
-#include <vector>
 #include <cassert>
-#include "test_macros.h"
-#include "test_allocator.h"
+#include <vector>
+
 #include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+template <unsigned N, class A>
+TEST_CONSTEXPR_CXX20 void test(const A& a1, const A& a2) {
+  std::vector<bool, A> l(N, true, a1);
+  std::vector<bool, A> lo(N, true, a1);
+  for (unsigned i = 1; i < N; i += 2) {
+    l[i]  = false;
+    lo[i] = false;
+  }
+  std::vector<bool, A> l2(std::move(l), a2);
+  assert(l2 == lo);
+  if (a1 == a2)
+    assert(l.empty());
+  else
+    LIBCPP_ASSERT(!l.empty()); // For incompatible allocators, l is not guaranteed to be empty after the move.
+  assert(l2.get_allocator() == a2);
+}
+
+TEST_CONSTEXPR_CXX20 bool tests() {
+  { // Test with default allocator: compatible allocators
+    using A = std::allocator<bool>;
+    test<5>(A(), A());
+    test<17>(A(), A());
+    test<65>(A(), A());
+    test<257>(A(), A());
+  }
+
+  { // Test with test_allocator: compatible and incompatible allocators
+    using A = test_allocator<bool>;
+
+    // Compatible allocators
+    test<5>(A(5), A(5));
+    test<17>(A(5), A(5));
+    test<65>(A(5), A(5));
+    test<257>(A(5), A(5));
+
+    // Incompatible allocators
+    test<5>(A(5), A(6));
+    test<17>(A(5), A(6));
+    test<65>(A(5), A(6));
+    test<257>(A(5), A(6));
+  }
+
+  { // Test with other_allocator: compatible and incompatible allocators
+    using A = other_allocator<bool>;
+
+    // Compatible allocators
+    test<5>(A(5), A(5));
+    test<17>(A(5), A(5));
+    test<65>(A(5), A(5));
+    test<257>(A(5), A(5));
+
+    // Incompatible allocators
+    test<5>(A(5), A(3));
+    test<17>(A(5), A(3));
+    test<65>(A(5), A(3));
+    test<257>(A(5), A(3));
+  }
+
+  { // Test with min_allocator: compatible allocators
+    using A = min_allocator<bool>;
+    test<5>(A(), A());
+    test<17>(A(), A());
+    test<65>(A(), A());
+    test<257>(A(), A());
+  }
 
-TEST_CONSTEXPR_CXX20 bool tests()
-{
-    {
-        std::vector<bool, test_allocator<bool> > l(test_allocator<bool>(5));
-        std::vector<bool, test_allocator<bool> > lo(test_allocator<bool>(5));
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(i);
-            lo.push_back(i);
-        }
-        std::vector<bool, test_allocator<bool> > l2(std::move(l), test_allocator<bool>(6));
-        assert(l2 == lo);
-        assert(!l.empty());
-        assert(l2.get_allocator() == test_allocator<bool>(6));
-    }
-    {
-        std::vector<bool, test_allocator<bool> > l(test_allocator<bool>(5));
-        std::vector<bool, test_allocator<bool> > lo(test_allocator<bool>(5));
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(i);
-            lo.push_back(i);
-        }
-        std::vector<bool, test_allocator<bool> > l2(std::move(l), test_allocator<bool>(5));
-        assert(l2 == lo);
-        assert(l.empty());
-        assert(l2.get_allocator() == test_allocator<bool>(5));
-    }
-    {
-        std::vector<bool, other_allocator<bool> > l(other_allocator<bool>(5));
-        std::vector<bool, other_allocator<bool> > lo(other_allocator<bool>(5));
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(i);
-            lo.push_back(i);
-        }
-        std::vector<bool, other_allocator<bool> > l2(std::move(l), other_allocator<bool>(4));
-        assert(l2 == lo);
-        assert(!l.empty());
-        assert(l2.get_allocator() == other_allocator<bool>(4));
-    }
-    {
-        std::vector<bool, min_allocator<bool> > l(min_allocator<bool>{});
-        std::vector<bool, min_allocator<bool> > lo(min_allocator<bool>{});
-        for (int i = 1; i <= 3; ++i)
-        {
-            l.push_back(i);
-            lo.push_back(i);
-        }
-        std::vector<bool, min_allocator<bool> > l2(std::move(l), min_allocator<bool>());
-        assert(l2 == lo);
-        assert(l.empty());
-        assert(l2.get_allocator() == min_allocator<bool>());
-    }
-
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
-    tests();
-#if TEST_STD_VER > 17
-    static_assert(tests());
+int main(int, char**) {
+  tests();
+#if TEST_STD_VER >= 20
+  static_assert(tests());
 #endif
-    return 0;
+  return 0;
 }



More information about the libcxx-commits mailing list