[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
Sun Jan 26 12:47:12 PST 2025
https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/120132
>From 42c5224ad94ffafd0fd9044d27e644118bfb2a77 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/4] 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 4f1c442ce0be8d..c3133ade40ae60 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);
@@ -695,25 +697,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>
@@ -758,9 +765,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 783ef787f549e509067a05b6cb665b533562037f 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/4] 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 c3133ade40ae60..c00f0af1dd0ab2 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);
@@ -697,30 +695,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>
@@ -765,8 +760,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;
}
>From ce52b987b759812b5e2070dad844a76363c39ba3 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Tue, 21 Jan 2025 10:54:34 -0500
Subject: [PATCH 3/4] Remove move-ctor benchmarks accoding to ldionne's
comments
---
.../containers/ContainerBenchmarks.h | 20 -------------------
.../vector_bool_operations.bench.cpp | 6 +-----
2 files changed, 1 insertion(+), 25 deletions(-)
diff --git a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
index a9ae36e43da8b9..49a1ea5204a15f 100644
--- a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
+++ b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h
@@ -39,16 +39,6 @@ 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);
@@ -59,16 +49,6 @@ void BM_CopyConstruct_Alloc(benchmark::State& st, Container, Allocator a) {
}
}
-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
index 7de6b344d43943..798d966047f5f6 100644
--- a/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
+++ b/libcxx/test/benchmarks/containers/vector_bool_operations.bench.cpp
@@ -25,12 +25,8 @@
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
+BENCHMARK_MAIN();
>From 0c7e214537775e935acaf2167f5fb41f6a01797e Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sun, 26 Jan 2025 15:42:43 -0500
Subject: [PATCH 4/4] Call standard std::copy and update size explicitly in
reserve
---
libcxx/include/__vector/vector_bool.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index c00f0af1dd0ab2..8dc288b8b50ce2 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -858,7 +858,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::reserve(size_type _
this->__throw_length_error();
vector __v(this->get_allocator());
__v.__vallocate(__n);
- __v.__construct_at_end(this->begin(), this->end(), this->size());
+ std::copy(__begin_, __begin_ + __external_cap_to_internal(size()), __v.__begin_);
+ __v.__size_ = size();
swap(__v);
}
}
More information about the libcxx-commits
mailing list