[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
Fri Mar 14 08:47:17 PDT 2025


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

>From 84025be070497af6bb68b38e841c809578f79ad9 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Fri, 14 Mar 2025 11:29:47 -0400
Subject: [PATCH] Enhance tests for copy/move constructors for vector<bool>

---
 .../sequences/vector.bool/copy.pass.cpp       |  76 +++++++-----
 .../sequences/vector.bool/copy_alloc.pass.cpp |  74 +++++++-----
 .../sequences/vector.bool/move.pass.cpp       |  83 +++++++------
 .../sequences/vector.bool/move_alloc.pass.cpp | 110 ++++++++++--------
 4 files changed, 204 insertions(+), 139 deletions(-)

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 15b26e8054df8..ba957471927c3 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/copy.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/copy.pass.cpp
@@ -11,12 +11,13 @@
 
 // vector(const vector& v);
 
-#include <vector>
+#include <array>
 #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) {
@@ -25,39 +26,56 @@ TEST_CONSTEXPR_CXX20 void test(const C& x) {
   LIBCPP_ASSERT(c.__invariants());
   assert(c.size() == s);
   assert(c == x);
+#if TEST_STD_VER >= 11
+  assert(c.get_allocator() ==
+         std::allocator_traits<typename C::allocator_type>::select_on_container_copy_construction(x.get_allocator()));
+#endif
 }
 
 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());
+  std::array<int, 5> a1   = {1, 0, 1, 0, 1};
+  std::array<int, 18> a2  = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
+  std::array<int, 33> a3  = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
+  std::array<int, 65> a4  = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
+  std::array<int, 299> a5 = {};
+  for (unsigned i = 0; i < a5.size(); i += 2)
+    a5[i] = 1;
+
+  // Tests for vector<bool> copy constructor with word size up to 5 (i.e., bit size > 256 on a 64-bit system)
+  { // Test with default std::allocator
+    test(std::vector<bool>(a1.begin(), a1.end()));
+    test(std::vector<bool>(a2.begin(), a2.end()));
+    test(std::vector<bool>(a3.begin(), a3.end()));
+    test(std::vector<bool>(a4.begin(), a4.end()));
+    test(std::vector<bool>(a5.begin(), a5.end()));
   }
-#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));
+  { // Test with test_allocator
+    using A = test_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a1.begin(), a1.end()));
+    test(C(a2.begin(), a2.end()));
+    test(C(a3.begin(), a3.end()));
+    test(C(a4.begin(), a4.end()));
+    test(C(a5.begin(), a5.end()));
   }
-  {
-    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));
+  { // Test with other_allocator
+    using A = other_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a1.begin(), a1.end()));
+    test(C(a2.begin(), a2.end()));
+    test(C(a3.begin(), a3.end()));
+    test(C(a4.begin(), a4.end()));
+    test(C(a5.begin(), a5.end()));
   }
-  {
-    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());
+  { // Test with min_allocator
+    using A = min_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a1.begin(), a1.end()));
+    test(C(a2.begin(), a2.end()));
+    test(C(a3.begin(), a3.end()));
+    test(C(a4.begin(), a4.end()));
+    test(C(a5.begin(), a5.end()));
   }
-#endif
 
   return true;
 }
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 d22b7c5d58447..17967ff2713cd 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,15 +7,17 @@
 //===----------------------------------------------------------------------===//
 
 // <vector>
+// vector<bool>
 
 // vector(const vector& v, const allocator_type& a);
 
-#include <vector>
+#include <array>
 #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) {
@@ -24,39 +26,53 @@ TEST_CONSTEXPR_CXX20 void test(const C& x, const typename C::allocator_type& a)
   LIBCPP_ASSERT(c.__invariants());
   assert(c.size() == s);
   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::array<int, 5> a1   = {1, 0, 1, 0, 1};
+  std::array<int, 18> a2  = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
+  std::array<int, 33> a3  = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
+  std::array<int, 65> a4  = {0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0};
+  std::array<int, 299> a5 = {};
+  for (unsigned i = 0; i < a5.size(); i += 2)
+    a5[i] = 1;
+
+  // Tests for allocator-extended copy constructor with word size up to 5 (i.e., bit size > 256 on a 64-bit system)
+  { // Test with the default std::allocator
+    test(std::vector<bool>(a1.begin(), a1.end()), std::allocator<bool>());
+    test(std::vector<bool>(a2.begin(), a2.end()), std::allocator<bool>());
+    test(std::vector<bool>(a3.begin(), a3.end()), std::allocator<bool>());
+    test(std::vector<bool>(a4.begin(), a4.end()), std::allocator<bool>());
+    test(std::vector<bool>(a5.begin(), a5.end()), std::allocator<bool>());
   }
-  {
-    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));
+  { // Test with test_allocator
+    using A = test_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a1.begin(), a1.end(), A(5)), A(3));
+    test(C(a2.begin(), a2.end(), A(5)), A(3));
+    test(C(a3.begin(), a3.end(), A(5)), A(3));
+    test(C(a4.begin(), a4.end(), A(5)), A(3));
+    test(C(a5.begin(), a5.end(), A(5)), A(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>());
+  { // Test with other_allocator
+    using A = other_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a1.begin(), a1.end(), A(5)), A(3));
+    test(C(a2.begin(), a2.end(), A(5)), A(3));
+    test(C(a3.begin(), a3.end(), A(5)), A(3));
+    test(C(a4.begin(), a4.end(), A(5)), A(3));
+    test(C(a5.begin(), a5.end(), A(5)), A(3));
   }
-  {
-    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>());
+  { // Test with min_allocator
+    using A = min_allocator<bool>;
+    using C = std::vector<bool, A>;
+    test(C(a1.begin(), a1.end(), A()), A());
+    test(C(a2.begin(), a2.end(), A()), A());
+    test(C(a3.begin(), a3.end(), A()), A());
+    test(C(a4.begin(), a4.end(), A()), A());
+    test(C(a5.begin(), a5.end(), A()), A());
   }
-#endif
 
   return true;
 }
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 a69f0b5738986..96bd37f2566fb 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/move.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/move.pass.cpp
@@ -9,54 +9,70 @@
 // UNSUPPORTED: c++03
 
 // <vector>
+// vector<bool>
 
 // vector(vector&& c);
 
-#include <vector>
+#include <array>
 #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& a) {
+  std::vector<bool, A> v(N, false, a);
+  std::vector<bool, A> v0(N, false, a);
+  for (unsigned i = 1; i < N; i += 2) {
+    v[i]  = true;
+    v0[i] = true;
+  }
+  std::vector<bool, A> v2 = std::move(v);
+  assert(v2 == v0);
+  assert(v.empty()); // The moved-from vector is guarantted to be empty after move-construction
+  assert(v2.get_allocator() == v0.get_allocator());
+}
 
 TEST_CONSTEXPR_CXX20 bool tests() {
   test_allocator_statistics alloc_stats;
+
+  // Tests for move constructor with word size up to 5 (i.e., bit size > 256 for 64-bit system)
   {
-    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());
+    using A = std::allocator<bool>;
+    test<5>(A());
+    test<18>(A());
+    test<33>(A());
+    test<65>(A());
+    test<299>(A());
   }
   {
-    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());
+    using A = other_allocator<bool>;
+    test<5>(A(5));
+    test<18>(A(5));
+    test<33>(A(5));
+    test<65>(A(5));
+    test<299>(A(5));
   }
   {
-    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());
+    using A = min_allocator<bool>;
+    test<5>(A());
+    test<18>(A());
+    test<33>(A());
+    test<65>(A());
+    test<299>(A());
   }
   {
+    using A = test_allocator<bool>;
+    test<5>(A(5, &alloc_stats));
+    test<18>(A(5, &alloc_stats));
+    test<33>(A(5, &alloc_stats));
+    test<65>(A(5, &alloc_stats));
+    test<299>(A(5, &alloc_stats));
+  }
+
+  { // Tests to verify the allocator statistics after move
     alloc_stats.clear();
     using Vect   = std::vector<bool, test_allocator<bool> >;
     using AllocT = Vect::allocator_type;
@@ -83,7 +99,6 @@ TEST_CONSTEXPR_CXX20 bool tests() {
       const AllocT& a2 = v2.get_allocator();
       assert(a2.get_id() == 101);
       assert(a2.get_data() == 42);
-
       assert(a1 == a2);
     }
   }
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 01e7410040686..f2d576c092c09 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,63 +9,79 @@
 // UNSUPPORTED: c++03
 
 // <vector>
+// vector<bool>
 
 // vector(vector&& c, const allocator_type& a);
 
-#include <vector>
+#include <array>
 #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& a, const A& a0) {
+  std::vector<bool, A> v(N, false, a);
+  std::vector<bool, A> v0(N, false, a0);
+  for (unsigned i = 1; i < N; i += 2) {
+    v[i]  = true;
+    v0[i] = true;
+  }
+  std::vector<bool, A> v2(std::move(v), a0);
+  assert(v2 == v0);
+  assert(v2.get_allocator() == a0);
+  if (a == a0)
+    assert(v.empty()); // After container-move, the vector is guarantted to be empty
+  else
+    LIBCPP_ASSERT(!v.empty()); // After element-wise move, the RHS vector is not necessarily empty
+}
 
 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));
+  { // Test with default allocator: compatible allocators
+    using A = std::allocator<bool>;
+    test<5>(A(), A());
+    test<17>(A(), A());
+    test<65>(A(), A());
+    test<299>(A(), A());
   }
-  {
-    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));
+  { // 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<299>(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<299>(A(5), A(6));
   }
-  {
-    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));
+  { // 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<299>(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<299>(A(5), A(3));
   }
-  {
-    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>());
+  { // Test with min_allocator: compatible allocators
+    using A = min_allocator<bool>;
+    test<5>(A(), A());
+    test<17>(A(), A());
+    test<65>(A(), A());
+    test<299>(A(), A());
   }
 
   return true;



More information about the libcxx-commits mailing list