Skip to content

Commit 929ccb3

Browse files
committed
Optimize bitset::to_string
1 parent 29c5e42 commit 929ccb3

File tree

2 files changed

+167
-6
lines changed

2 files changed

+167
-6
lines changed

libcxx/include/bitset

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ template <size_t N> struct hash<std::bitset<N>>;
136136
# include <__algorithm/fill_n.h>
137137
# include <__algorithm/find.h>
138138
# include <__assert>
139+
# include <__bit/countr.h>
140+
# include <__bit/invert_if.h>
139141
# include <__bit_reference>
140142
# include <__config>
141143
# include <__cstddef/ptrdiff_t.h>
@@ -223,6 +225,10 @@ protected:
223225
return to_ullong(integral_constant < bool, _Size< sizeof(unsigned long long) * CHAR_BIT>());
224226
}
225227

228+
template <bool _Spare, class _CharT, class _Traits, class _Allocator>
229+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
230+
__to_string(_CharT __zero, _CharT __one) const;
231+
226232
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT;
227233
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool any() const _NOEXCEPT;
228234
_LIBCPP_HIDE_FROM_ABI size_t __hash_code() const _NOEXCEPT;
@@ -389,6 +395,22 @@ __bitset<_N_words, _Size>::to_ullong(true_type, true_type) const {
389395
return __r;
390396
}
391397

398+
template <size_t _N_words, size_t _Size>
399+
template <bool _Spare, class _CharT, class _Traits, class _Allocator>
400+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
401+
__bitset<_N_words, _Size>::__to_string(_CharT __zero, _CharT __one) const {
402+
basic_string<_CharT, _Traits, _Allocator> __r(_Size, __zero);
403+
for (size_t __i = 0, __bits = 0; __i < _N_words; ++__i, __bits += __bits_per_word) {
404+
__storage_type __word = std::__invert_if<!_Spare>(__first_[__i]);
405+
if (__i == _N_words - 1 && _Size - __bits < __bits_per_word)
406+
__word &= (__storage_type(1) << (_Size - __bits)) - 1;
407+
for (; __word; __word &= (__word - 1))
408+
__r[_Size - 1 - (__bits + std::__countr_zero(__word))] = __one;
409+
}
410+
411+
return __r;
412+
}
413+
392414
template <size_t _N_words, size_t _Size>
393415
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool __bitset<_N_words, _Size>::all() const _NOEXCEPT {
394416
// do middle whole words
@@ -480,6 +502,10 @@ protected:
480502
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const;
481503
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const;
482504

505+
template <bool _Sparse, class _CharT, class _Traits, class _Allocator>
506+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
507+
__to_string(_CharT __zero, _CharT __one) const;
508+
483509
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT;
484510
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool any() const _NOEXCEPT;
485511

@@ -529,6 +555,21 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long __
529555
return __first_;
530556
}
531557

558+
template <size_t _Size>
559+
template <bool _Spare, class _CharT, class _Traits, class _Allocator>
560+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
561+
__bitset<1, _Size>::__to_string(_CharT __zero, _CharT __one) const {
562+
basic_string<_CharT, _Traits, _Allocator> __r(_Size, __zero);
563+
__storage_type __word = std::__invert_if<!_Spare>(__first_);
564+
if (_Size < __bits_per_word)
565+
__word &= (__storage_type(1) << _Size) - 1;
566+
for (; __word; __word &= (__word - 1)) {
567+
size_t __pos = std::__countr_zero(__word);
568+
__r[_Size - 1 - __pos] = __one;
569+
}
570+
return __r;
571+
}
572+
532573
template <size_t _Size>
533574
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool __bitset<1, _Size>::all() const _NOEXCEPT {
534575
__storage_type __m = ~__storage_type(0) >> (__bits_per_word - _Size);
@@ -593,6 +634,12 @@ protected:
593634
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const { return 0; }
594635
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const { return 0; }
595636

637+
template <bool _Spare, class _CharT, class _Traits, class _Allocator>
638+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
639+
__to_string(_CharT, _CharT) const {
640+
return basic_string<_CharT, _Traits, _Allocator>();
641+
}
642+
596643
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT { return true; }
597644
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool any() const _NOEXCEPT { return false; }
598645

@@ -848,12 +895,11 @@ template <size_t _Size>
848895
template <class _CharT, class _Traits, class _Allocator>
849896
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
850897
bitset<_Size>::to_string(_CharT __zero, _CharT __one) const {
851-
basic_string<_CharT, _Traits, _Allocator> __r(_Size, __zero);
852-
for (size_t __i = 0; __i != _Size; ++__i) {
853-
if ((*this)[__i])
854-
__r[_Size - 1 - __i] = __one;
855-
}
856-
return __r;
898+
bool __sparse = size_t(std::count(__base::__make_iter(0), __base::__make_iter(_Size), true)) < _Size / 2;
899+
if (__sparse)
900+
return __base::template __to_string<true, _CharT, _Traits, _Allocator>(__zero, __one);
901+
else
902+
return __base::template __to_string<false, _CharT, _Traits, _Allocator>(__one, __zero);
857903
}
858904

859905
template <size_t _Size>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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
10+
11+
#include "benchmark/benchmark.h"
12+
#include <bitset>
13+
#include <cmath>
14+
#include <cstddef>
15+
16+
template <std::size_t N>
17+
struct GenerateBitset {
18+
// Construct a bitset with p*N true bits
19+
static std::bitset<N> generate(double p) {
20+
std::bitset<N> b;
21+
if (p <= 0.0)
22+
return b;
23+
if (p >= 1.0)
24+
return ~b;
25+
26+
std::size_t num_ones = std::round(N * p);
27+
if (num_ones == 0)
28+
return b;
29+
30+
double step = static_cast<double>(N) / num_ones;
31+
double error = 0.0;
32+
33+
std::size_t pos = 0;
34+
for (std::size_t i = 0; i < num_ones; ++i) {
35+
if (pos >= N)
36+
break;
37+
b.set(pos);
38+
error += step;
39+
pos += std::floor(error);
40+
error -= std::floor(error);
41+
}
42+
return b;
43+
}
44+
45+
static std::bitset<N> sparse() { return generate(0.1); }
46+
static std::bitset<N> dense() { return generate(0.9); }
47+
static std::bitset<N> uniform() { return generate(0.5); }
48+
};
49+
50+
template <std::size_t N>
51+
static void BM_BitsetToString(benchmark::State& state) {
52+
double p = state.range(0) / 100.0;
53+
std::bitset<N> b = GenerateBitset<N>::generate(p);
54+
benchmark::DoNotOptimize(b);
55+
56+
for (auto _ : state) {
57+
benchmark::DoNotOptimize(b.to_string());
58+
}
59+
}
60+
61+
// Sparse bitset
62+
BENCHMARK(BM_BitsetToString<32>)->Arg(10)->Name("BM_BitsetToString<32>/Sparse (10%)");
63+
BENCHMARK(BM_BitsetToString<64>)->Arg(10)->Name("BM_BitsetToString<64>/Sparse (10%)");
64+
BENCHMARK(BM_BitsetToString<128>)->Arg(10)->Name("BM_BitsetToString<128>/Sparse (10%)");
65+
BENCHMARK(BM_BitsetToString<256>)->Arg(10)->Name("BM_BitsetToString<256>/Sparse (10%)");
66+
BENCHMARK(BM_BitsetToString<512>)->Arg(10)->Name("BM_BitsetToString<512>/Sparse (10%)");
67+
BENCHMARK(BM_BitsetToString<1024>)->Arg(10)->Name("BM_BitsetToString<1024>/Sparse (10%)");
68+
BENCHMARK(BM_BitsetToString<2048>)->Arg(10)->Name("BM_BitsetToString<2048>/Sparse (10%)");
69+
BENCHMARK(BM_BitsetToString<4096>)->Arg(10)->Name("BM_BitsetToString<4096>/Sparse (10%)");
70+
BENCHMARK(BM_BitsetToString<8192>)->Arg(10)->Name("BM_BitsetToString<8192>/Sparse (10%)");
71+
BENCHMARK(BM_BitsetToString<16384>)->Arg(10)->Name("BM_BitsetToString<16384>/Sparse (10%)");
72+
BENCHMARK(BM_BitsetToString<32768>)->Arg(10)->Name("BM_BitsetToString<32768>/Sparse (10%)");
73+
BENCHMARK(BM_BitsetToString<65536>)->Arg(10)->Name("BM_BitsetToString<65536>/Sparse (10%)");
74+
BENCHMARK(BM_BitsetToString<131072>)->Arg(10)->Name("BM_BitsetToString<131072>/Sparse (10%)");
75+
BENCHMARK(BM_BitsetToString<262144>)->Arg(10)->Name("BM_BitsetToString<262144>/Sparse (10%)");
76+
BENCHMARK(BM_BitsetToString<524288>)->Arg(10)->Name("BM_BitsetToString<524288>/Sparse (10%)");
77+
BENCHMARK(BM_BitsetToString<1048576>)->Arg(10)->Name("BM_BitsetToString<1048576>/Sparse (10%)"); // 1 << 20
78+
79+
// Dense bitset
80+
BENCHMARK(BM_BitsetToString<32>)->Arg(90)->Name("BM_BitsetToString<32>/Dense (90%)");
81+
BENCHMARK(BM_BitsetToString<64>)->Arg(90)->Name("BM_BitsetToString<64>/Dense (90%)");
82+
BENCHMARK(BM_BitsetToString<128>)->Arg(90)->Name("BM_BitsetToString<128>/Dense (90%)");
83+
BENCHMARK(BM_BitsetToString<256>)->Arg(90)->Name("BM_BitsetToString<256>/Dense (90%)");
84+
BENCHMARK(BM_BitsetToString<512>)->Arg(90)->Name("BM_BitsetToString<512>/Dense (90%)");
85+
BENCHMARK(BM_BitsetToString<1024>)->Arg(90)->Name("BM_BitsetToString<1024>/Dense (90%)");
86+
BENCHMARK(BM_BitsetToString<2048>)->Arg(90)->Name("BM_BitsetToString<2048>/Dense (90%)");
87+
BENCHMARK(BM_BitsetToString<4096>)->Arg(90)->Name("BM_BitsetToString<4096>/Dense (90%)");
88+
BENCHMARK(BM_BitsetToString<8192>)->Arg(90)->Name("BM_BitsetToString<8192>/Dense (90%)");
89+
BENCHMARK(BM_BitsetToString<16384>)->Arg(90)->Name("BM_BitsetToString<16384>/Dense (90%)");
90+
BENCHMARK(BM_BitsetToString<32768>)->Arg(90)->Name("BM_BitsetToString<32768>/Dense (90%)");
91+
BENCHMARK(BM_BitsetToString<65536>)->Arg(90)->Name("BM_BitsetToString<65536>/Dense (90%)");
92+
BENCHMARK(BM_BitsetToString<131072>)->Arg(90)->Name("BM_BitsetToString<131072>/Dense (90%)");
93+
BENCHMARK(BM_BitsetToString<262144>)->Arg(90)->Name("BM_BitsetToString<262144>/Dense (90%)");
94+
BENCHMARK(BM_BitsetToString<524288>)->Arg(90)->Name("BM_BitsetToString<524288>/Dense (90%)");
95+
BENCHMARK(BM_BitsetToString<1048576>)->Arg(90)->Name("BM_BitsetToString<1048576>/Dense (90%)"); // 1 << 20
96+
97+
// Uniform bitset
98+
BENCHMARK(BM_BitsetToString<32>)->Arg(50)->Name("BM_BitsetToString<32>/Uniform (50%)");
99+
BENCHMARK(BM_BitsetToString<64>)->Arg(50)->Name("BM_BitsetToString<64>/Uniform (50%)");
100+
BENCHMARK(BM_BitsetToString<128>)->Arg(50)->Name("BM_BitsetToString<128>/Uniform (50%)");
101+
BENCHMARK(BM_BitsetToString<256>)->Arg(50)->Name("BM_BitsetToString<256>/Uniform (50%)");
102+
BENCHMARK(BM_BitsetToString<512>)->Arg(50)->Name("BM_BitsetToString<512>/Uniform (50%)");
103+
BENCHMARK(BM_BitsetToString<1024>)->Arg(50)->Name("BM_BitsetToString<1024>/Uniform (50%)");
104+
BENCHMARK(BM_BitsetToString<2048>)->Arg(50)->Name("BM_BitsetToString<2048>/Uniform (50%)");
105+
BENCHMARK(BM_BitsetToString<4096>)->Arg(50)->Name("BM_BitsetToString<4096>/Uniform (50%)");
106+
BENCHMARK(BM_BitsetToString<8192>)->Arg(50)->Name("BM_BitsetToString<8192>/Uniform (50%)");
107+
BENCHMARK(BM_BitsetToString<16384>)->Arg(50)->Name("BM_BitsetToString<16384>/Uniform (50%)");
108+
BENCHMARK(BM_BitsetToString<32768>)->Arg(50)->Name("BM_BitsetToString<32768>/Uniform (50%)");
109+
BENCHMARK(BM_BitsetToString<65536>)->Arg(50)->Name("BM_BitsetToString<65536>/Uniform (50%)");
110+
BENCHMARK(BM_BitsetToString<131072>)->Arg(50)->Name("BM_BitsetToString<131072>/Uniform (50%)");
111+
BENCHMARK(BM_BitsetToString<262144>)->Arg(50)->Name("BM_BitsetToString<262144>/Uniform (50%)");
112+
BENCHMARK(BM_BitsetToString<524288>)->Arg(50)->Name("BM_BitsetToString<524288>/Uniform (50%)");
113+
BENCHMARK(BM_BitsetToString<1048576>)->Arg(50)->Name("BM_BitsetToString<1048576>/Uniform (50%)"); // 1 << 20
114+
115+
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)