Skip to content

Commit c4f312e

Browse files
committed
Speed-up and refactor move-assignment operator for vector<bool>
1 parent f75c846 commit c4f312e

File tree

6 files changed

+283
-105
lines changed

6 files changed

+283
-105
lines changed

libcxx/include/__vector/vector_bool.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
398398
__guard.__complete();
399399
}
400400

401+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __realloc_and_copy(const vector& __v);
402+
401403
template <class _Iterator, class _Sentinel>
402404
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last);
403405

@@ -527,7 +529,7 @@ vector<bool, _Allocator>::__recommend(size_type __new_size) const {
527529
const size_type __cap = capacity();
528530
if (__cap >= __ms / 2)
529531
return __ms;
530-
return std::max(2 * __cap, __align_it(__new_size));
532+
return std::max<size_type>(2 * __cap, __align_it(__new_size));
531533
}
532534

533535
// Default constructs __n objects starting at __end_
@@ -695,18 +697,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const vector& __v
695697
}
696698
}
697699

700+
// This function copies each storage word as a whole, which is substantially more efficient than copying
701+
// individual bits within each word.
702+
template <class _Allocator>
703+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
704+
vector<bool, _Allocator>::__realloc_and_copy(const vector& __v) {
705+
if (__v.__size_) {
706+
if (__v.__size_ > capacity()) {
707+
__vdeallocate();
708+
__vallocate(__v.__size_);
709+
}
710+
std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.__size_), __begin_);
711+
}
712+
__size_ = __v.__size_;
713+
}
714+
698715
template <class _Allocator>
699716
_LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>& vector<bool, _Allocator>::operator=(const vector& __v) {
700717
if (this != std::addressof(__v)) {
701718
__copy_assign_alloc(__v);
702-
if (__v.__size_) {
703-
if (__v.__size_ > capacity()) {
704-
__vdeallocate();
705-
__vallocate(__v.__size_);
706-
}
707-
std::copy(__v.__begin_, __v.__begin_ + __external_cap_to_internal(__v.__size_), __begin_);
708-
}
709-
__size_ = __v.__size_;
719+
__realloc_and_copy(__v);
710720
}
711721
return *this;
712722
}
@@ -754,7 +764,7 @@ vector<bool, _Allocator>::operator=(vector&& __v)
754764
template <class _Allocator>
755765
_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vector& __c, false_type) {
756766
if (__alloc_ != __c.__alloc_)
757-
assign(__c.begin(), __c.end());
767+
__realloc_and_copy(__c);
758768
else
759769
__move_assign(__c, true_type());
760770
}

libcxx/test/benchmarks/containers/ContainerBenchmarks.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ void BM_Assignment(benchmark::State& st, Container) {
5151
}
5252
}
5353

54+
template <class Container, class Allocator>
55+
void BM_Move_Assignment(benchmark::State& st, Container, Allocator) {
56+
auto size = st.range(0);
57+
Container c1(Allocator{1});
58+
Container c2(Allocator{2});
59+
// c1.reserve(size);
60+
c2.resize(size);
61+
for (auto _ : st) {
62+
c1 = std::move(c2);
63+
DoNotOptimizeData(c1);
64+
DoNotOptimizeData(c2);
65+
}
66+
}
67+
5468
template <std::size_t... sz, typename Container, typename GenInputs>
5569
void BM_AssignInputIterIter(benchmark::State& st, Container c, GenInputs gen) {
5670
auto v = gen(1, sz...);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
#include <cstdint>
12+
#include <cstdlib>
13+
#include <cstring>
14+
#include <deque>
15+
#include <functional>
16+
#include <memory>
17+
#include <string>
18+
#include <vector>
19+
20+
#include "benchmark/benchmark.h"
21+
#include "ContainerBenchmarks.h"
22+
#include "../GenerateInput.h"
23+
#include "sized_allocator.h"
24+
#include "test_allocator.h"
25+
26+
using namespace ContainerBenchmarks;
27+
28+
BENCHMARK_CAPTURE(BM_Move_Assignment,
29+
vector_bool_uint16_t,
30+
std::vector<bool, sized_allocator<bool, std::uint16_t, std::int16_t>>{},
31+
sized_allocator<bool, std::uint16_t, std::int16_t>{})
32+
->Arg(5140480);
33+
34+
BENCHMARK_CAPTURE(BM_Move_Assignment,
35+
vector_bool_size_t,
36+
std::vector<bool, sized_allocator<bool, std::size_t, std::ptrdiff_t>>{},
37+
sized_allocator<bool, std::size_t, std::ptrdiff_t>{})
38+
->Arg(5140480);
39+
40+
BENCHMARK_MAIN();

libcxx/test/std/containers/sequences/vector.bool/assign_copy.pass.cpp

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,65 @@
1010

1111
// vector& operator=(const vector& c);
1212

13-
#include <vector>
1413
#include <cassert>
15-
#include "test_macros.h"
16-
#include "test_allocator.h"
14+
#include <vector>
15+
1716
#include "min_allocator.h"
17+
#include "test_allocator.h"
18+
#include "test_macros.h"
1819

19-
TEST_CONSTEXPR_CXX20 bool tests()
20-
{
21-
{
22-
std::vector<bool, test_allocator<bool> > l(3, true, test_allocator<bool>(5));
23-
std::vector<bool, test_allocator<bool> > l2(l, test_allocator<bool>(3));
24-
l2 = l;
25-
assert(l2 == l);
26-
assert(l2.get_allocator() == test_allocator<bool>(3));
27-
}
28-
{
29-
std::vector<bool, other_allocator<bool> > l(3, true, other_allocator<bool>(5));
30-
std::vector<bool, other_allocator<bool> > l2(l, other_allocator<bool>(3));
31-
l2 = l;
32-
assert(l2 == l);
33-
assert(l2.get_allocator() == other_allocator<bool>(5));
34-
}
20+
TEST_CONSTEXPR_CXX20 bool tests() {
21+
// Test with insufficient space where reallocation occurs during assignment
22+
{
23+
std::vector<bool, test_allocator<bool> > l(3, true, test_allocator<bool>(5));
24+
std::vector<bool, test_allocator<bool> > l2(l, test_allocator<bool>(3));
25+
l2 = l;
26+
assert(l2 == l);
27+
assert(l2.get_allocator() == test_allocator<bool>(3));
28+
}
29+
{
30+
std::vector<bool, other_allocator<bool> > l(3, true, other_allocator<bool>(5));
31+
std::vector<bool, other_allocator<bool> > l2(l, other_allocator<bool>(3));
32+
l2 = l;
33+
assert(l2 == l);
34+
assert(l2.get_allocator() == other_allocator<bool>(5));
35+
}
3536
#if TEST_STD_VER >= 11
36-
{
37-
std::vector<bool, min_allocator<bool> > l(3, true, min_allocator<bool>());
38-
std::vector<bool, min_allocator<bool> > l2(l, min_allocator<bool>());
39-
l2 = l;
40-
assert(l2 == l);
41-
assert(l2.get_allocator() == min_allocator<bool>());
42-
}
37+
{
38+
std::vector<bool, min_allocator<bool> > l(3, true, min_allocator<bool>());
39+
std::vector<bool, min_allocator<bool> > l2(l, min_allocator<bool>());
40+
l2 = l;
41+
assert(l2 == l);
42+
assert(l2.get_allocator() == min_allocator<bool>());
43+
}
4344
#endif
4445

45-
return true;
46+
// Test with sufficient size where no reallocation occurs during assignment
47+
{
48+
std::vector<bool, test_allocator<bool> > l(4, true, test_allocator<bool>(5));
49+
std::vector<bool, test_allocator<bool> > l2(5, false, test_allocator<bool>(3));
50+
l2 = l;
51+
assert(l2 == l);
52+
assert(l2.get_allocator() == test_allocator<bool>(3));
53+
}
54+
55+
// Test with sufficient spare space where no reallocation occurs during assignment
56+
{
57+
std::vector<bool, test_allocator<bool> > l(4, true, test_allocator<bool>(5));
58+
std::vector<bool, test_allocator<bool> > l2(2, false, test_allocator<bool>(3));
59+
l2.reserve(5);
60+
l2 = l;
61+
assert(l2 == l);
62+
assert(l2.get_allocator() == test_allocator<bool>(3));
63+
}
64+
65+
return true;
4666
}
4767

48-
int main(int, char**)
49-
{
50-
tests();
68+
int main(int, char**) {
69+
tests();
5170
#if TEST_STD_VER > 17
52-
static_assert(tests());
71+
static_assert(tests());
5372
#endif
54-
return 0;
73+
return 0;
5574
}

0 commit comments

Comments
 (0)