Skip to content

Commit 18aab0b

Browse files
committed
Speed-up input_iterator-pair ctor and assign functions
1 parent f5bd623 commit 18aab0b

File tree

5 files changed

+148
-24
lines changed

5 files changed

+148
-24
lines changed

Diff for: libcxx/include/__vector/vector_bool.h

+35-4
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,7 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
411411
__init_with_sentinel(_InputIterator __first, _Sentinel __last) {
412412
auto __guard = std::__make_exception_guard(__destroy_vector(*this));
413413

414-
for (; __first != __last; ++__first)
415-
push_back(*__first);
414+
__push_back_words_with_sentinel(std::move(__first), std::move(__last));
416415

417416
__guard.__complete();
418417
}
@@ -509,6 +508,10 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
509508

510509
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector&, false_type) _NOEXCEPT {}
511510

511+
template <class _InputIterator, class _Sentinel>
512+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
513+
__push_back_words_with_sentinel(_InputIterator __first, _Sentinel __last);
514+
512515
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_t __hash_code() const _NOEXCEPT;
513516

514517
friend class __bit_reference<vector>;
@@ -820,8 +823,7 @@ template <class _Iterator, class _Sentinel>
820823
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
821824
vector<bool, _Allocator>::__assign_with_sentinel(_Iterator __first, _Sentinel __last) {
822825
clear();
823-
for (; __first != __last; ++__first)
824-
push_back(*__first);
826+
__push_back_words_with_sentinel(std::move(__first), std::move(__last));
825827
}
826828

827829
template <class _Allocator>
@@ -1084,6 +1086,35 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::flip() _NOEXCEPT {
10841086
*__p = ~*__p;
10851087
}
10861088

1089+
// Push bits from the range [__first, __last) into the vector in word-sized chunks.
1090+
// Precondition: The size of the vector must be a multiple of `__bits_per_word`,
1091+
// implying that the vector can only accommodate full words of bits.
1092+
//
1093+
// This function iterates through the input range, collecting bits until a full
1094+
// word is formed or the end of the range is reached. It then stores the word
1095+
// in the vector's internal storage, reallocating if necessary.
1096+
template <class _Allocator>
1097+
template <class _InputIterator, class _Sentinel>
1098+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
1099+
vector<bool, _Allocator>::__push_back_words_with_sentinel(_InputIterator __first, _Sentinel __last) {
1100+
_LIBCPP_ASSERT_VALID_INPUT_RANGE(
1101+
this->__size_ % __bits_per_word == 0,
1102+
"vector<bool>::__push_back_words_with_sentinel called with a size that is not a multiple of __bits_per_word");
1103+
unsigned __n_words = this->__size_ / __bits_per_word;
1104+
while (__first != __last) {
1105+
__storage_type __w = 0;
1106+
unsigned __ctz = 0;
1107+
for (; __ctz != __bits_per_word && __first != __last; ++__ctz, (void)++__first) {
1108+
if (*__first)
1109+
__w |= static_cast<__storage_type>(static_cast<__storage_type>(1) << __ctz);
1110+
}
1111+
if (this->__size_ == this->capacity())
1112+
reserve(__recommend(this->__size_ + 1));
1113+
this->__begin_[__n_words++] = __w;
1114+
this->__size_ += __ctz;
1115+
}
1116+
}
1117+
10871118
template <class _Allocator>
10881119
_LIBCPP_CONSTEXPR_SINCE_CXX20 bool vector<bool, _Allocator>::__invariants() const {
10891120
if (this->__begin_ == nullptr) {

Diff for: libcxx/test/benchmarks/containers/ContainerBenchmarks.h

+59-9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <utility>
1616

1717
#include "benchmark/benchmark.h"
18+
#include "../../std/containers/from_range_helpers.h"
1819
#include "../Utilities.h"
1920
#include "test_iterators.h"
2021

@@ -51,16 +52,42 @@ void BM_Assignment(benchmark::State& st, Container) {
5152
}
5253
}
5354

54-
template <std::size_t... sz, typename Container, typename GenInputs>
55-
void BM_AssignInputIterIter(benchmark::State& st, Container c, GenInputs gen) {
56-
auto v = gen(1, sz...);
57-
c.resize(st.range(0), v[0]);
58-
auto in = gen(st.range(1), sz...);
59-
benchmark::DoNotOptimize(&in);
60-
benchmark::DoNotOptimize(&c);
55+
template <typename Container, class Generator>
56+
void BM_AssignInputIterIter(benchmark::State& st, Generator gen) {
57+
using T = typename Container::value_type;
58+
auto size = st.range(0);
59+
auto in1 = gen(size);
60+
auto in2 = gen(size);
61+
DoNotOptimizeData(in1);
62+
DoNotOptimizeData(in2);
63+
Container c(in1.begin(), in1.end());
64+
bool toggle = false;
6165
for (auto _ : st) {
62-
c.assign(cpp17_input_iterator(in.begin()), cpp17_input_iterator(in.end()));
63-
benchmark::ClobberMemory();
66+
std::vector<T>& in = toggle ? in1 : in2;
67+
auto first = in.begin();
68+
auto last = in.end();
69+
c.assign(cpp17_input_iterator(first), cpp17_input_iterator(last));
70+
toggle = !toggle;
71+
DoNotOptimizeData(c);
72+
}
73+
}
74+
75+
template <typename Container, class Generator>
76+
void BM_AssignInputRange(benchmark::State& st, Generator gen) {
77+
auto size = st.range(0);
78+
auto in1 = gen(size);
79+
auto in2 = gen(size);
80+
DoNotOptimizeData(in1);
81+
DoNotOptimizeData(in2);
82+
input_only_range rg1(std::ranges::begin(in1), std::ranges::end(in1));
83+
input_only_range rg2(std::ranges::begin(in2), std::ranges::end(in2));
84+
Container c(std::from_range, rg1);
85+
bool toggle = false;
86+
for (auto _ : st) {
87+
auto& rg = toggle ? rg1 : rg2;
88+
c.assign_range(rg);
89+
toggle = !toggle;
90+
DoNotOptimizeData(c);
6491
}
6592
}
6693

@@ -85,6 +112,18 @@ void BM_ConstructIterIter(benchmark::State& st, Container, GenInputs gen) {
85112
}
86113
}
87114

115+
template <class Container, class GenInputs>
116+
void BM_ConstructInputIterIter(benchmark::State& st, GenInputs gen) {
117+
auto in = gen(st.range(0));
118+
const auto beg = cpp17_input_iterator(in.begin());
119+
const auto end = cpp17_input_iterator(in.end());
120+
benchmark::DoNotOptimize(&in);
121+
while (st.KeepRunning()) {
122+
Container c(beg, end);
123+
DoNotOptimizeData(c);
124+
}
125+
}
126+
88127
template <class Container, class GenInputs>
89128
void BM_ConstructFromRange(benchmark::State& st, Container, GenInputs gen) {
90129
auto in = gen(st.range(0));
@@ -95,6 +134,17 @@ void BM_ConstructFromRange(benchmark::State& st, Container, GenInputs gen) {
95134
}
96135
}
97136

137+
template <class Container, class GenInputs>
138+
void BM_ConstructFromInputRange(benchmark::State& st, GenInputs gen) {
139+
auto in = gen(st.range(0));
140+
input_only_range rg(std::ranges::begin(in), std::ranges::end(in));
141+
benchmark::DoNotOptimize(&in);
142+
while (st.KeepRunning()) {
143+
Container c(std::from_range, rg);
144+
DoNotOptimizeData(c);
145+
}
146+
}
147+
98148
template <class Container>
99149
void BM_Pushback_no_grow(benchmark::State& state, Container c) {
100150
int count = state.range(0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
24+
using namespace ContainerBenchmarks;
25+
26+
BENCHMARK_CAPTURE(BM_ConstructInputIterIter<std::vector<bool>>, vb, getRandomIntegerInputs<bool>)->Arg(514048);
27+
BENCHMARK_CAPTURE(BM_ConstructFromInputRange<std::vector<bool>>, vb, getRandomIntegerInputs<bool>)->Arg(514048);
28+
29+
BENCHMARK_CAPTURE(BM_AssignInputIterIter<std::vector<bool>>, vb, getRandomIntegerInputs<bool>)->Arg(514048);
30+
BENCHMARK_CAPTURE(BM_AssignInputRange<std::vector<bool>>, vb, getRandomIntegerInputs<bool>)->Arg(514048);
31+
32+
BENCHMARK_MAIN();

Diff for: libcxx/test/benchmarks/containers/vector_operations.bench.cpp

+4-11
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,11 @@ BENCHMARK(bm_grow<std::string>);
7878
BENCHMARK(bm_grow<std::unique_ptr<int>>);
7979
BENCHMARK(bm_grow<std::deque<int>>);
8080

81-
BENCHMARK_CAPTURE(BM_AssignInputIterIter, vector_int, std::vector<int>{}, getRandomIntegerInputs<int>)
82-
->Args({TestNumInputs, TestNumInputs});
81+
BENCHMARK_CAPTURE(BM_AssignInputIterIter<std::vector<int>>, vector_int, getRandomIntegerInputs<int>)
82+
->Arg(TestNumInputs);
8383

84-
BENCHMARK_CAPTURE(
85-
BM_AssignInputIterIter<32>, vector_string, std::vector<std::string>{}, getRandomStringInputsWithLength)
86-
->Args({TestNumInputs, TestNumInputs});
87-
88-
BENCHMARK_CAPTURE(BM_AssignInputIterIter<100>,
89-
vector_vector_int,
90-
std::vector<std::vector<int>>{},
91-
getRandomIntegerInputsWithLength<int>)
92-
->Args({TestNumInputs, TestNumInputs});
84+
BENCHMARK_CAPTURE(BM_AssignInputIterIter<std::vector<std::string>>, vector_string, getRandomStringInputs)
85+
->Arg(TestNumInputs);
9386

9487
BENCHMARK_CAPTURE(BM_Insert_InputIterIter_NoRealloc, vector_int, std::vector<int>(100, 1), getRandomIntegerInputs<int>)
9588
->Arg(514048);

Diff for: libcxx/test/std/containers/from_range_helpers.h

+18
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ constexpr auto wrap_input(std::vector<T>& input) {
5050
return std::ranges::subrange(std::move(b), std::move(e));
5151
}
5252

53+
template <class It>
54+
class input_only_range {
55+
public:
56+
using Iter = cpp20_input_iterator<It>;
57+
using Sent = sentinel_wrapper<Iter>;
58+
59+
input_only_range(It begin, It end) : begin_(std::move(begin)), end_(std::move(end)) {}
60+
Iter begin() { return Iter(std::move(begin_)); }
61+
Sent end() { return Sent(Iter(std::move(end_))); }
62+
63+
private:
64+
It begin_;
65+
It end_;
66+
};
67+
68+
template <class It>
69+
input_only_range(It, It) -> input_only_range<It>;
70+
5371
struct KeyValue {
5472
int key; // Only the key is considered for equality comparison.
5573
char value; // Allows distinguishing equivalent instances.

0 commit comments

Comments
 (0)