Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,5 @@ add_benchmark(vector_bool_copy_n src/vector_bool_copy_n.cpp)
add_benchmark(vector_bool_count src/vector_bool_count.cpp)
add_benchmark(vector_bool_meow_of src/vector_bool_meow_of.cpp)
add_benchmark(vector_bool_move src/vector_bool_move.cpp)
add_benchmark(vector_bool_permute src/vector_bool_permute.cpp)
add_benchmark(vector_bool_transform src/vector_bool_transform.cpp)
83 changes: 83 additions & 0 deletions benchmarks/src/vector_bool_permute.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <benchmark/benchmark.h>
//
#include <algorithm>
#include <cstddef>
#include <random>
#include <vector>

#include "utility.hpp"

using namespace std;

enum class equality { eq, neq };
enum class args { three, four };


template <equality Eq, args Args>
void perm_arr_check(benchmark::State& state) {
const auto size = static_cast<size_t>(state.range(0));
vector<bool> v1 = random_vector<bool>(size);
vector<bool> v2;
if constexpr (Eq == equality::eq) {
v2 = v1;
} else {
v2 = random_vector<bool>(size, 1u);
}

auto a1 = make_unique<bool[]>(size);
auto a2 = make_unique<bool[]>(size);
copy(v1.begin(), v1.end(), a1.get());
copy(v2.begin(), v2.end(), a2.get());

for (auto _ : state) {
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
bool r;
if constexpr (Args == args::three) {
r = is_permutation(a1.get(), a1.get() + size, a2.get());
} else {
r = is_permutation(a1.get(), a1.get() + size, a2.get(), a2.get() + size);
}
benchmark::DoNotOptimize(r);
}
}


template <equality Eq, args Args>
void perm_vbool_check(benchmark::State& state) {
const auto size = static_cast<size_t>(state.range(0));
vector<bool> v1 = random_vector<bool>(size);
vector<bool> v2;
if constexpr (Eq == equality::eq) {
v2 = v1;
} else {
v2 = random_vector<bool>(size, 1u);
}

for (auto _ : state) {
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
bool r;
if constexpr (Args == args::three) {
r = is_permutation(v1.begin(), v1.end(), v2.begin());
} else {
r = is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end());
}
benchmark::DoNotOptimize(r);
}
}

BENCHMARK(perm_arr_check<equality::eq, args::three>)->RangeMultiplier(64)->Range(64, 64 << 10);
BENCHMARK(perm_arr_check<equality::eq, args::four>)->RangeMultiplier(64)->Range(64, 64 << 10);
BENCHMARK(perm_arr_check<equality::neq, args::three>)->RangeMultiplier(64)->Range(64, 64 << 10);
BENCHMARK(perm_arr_check<equality::neq, args::four>)->RangeMultiplier(64)->Range(64, 64 << 10);

BENCHMARK(perm_vbool_check<equality::eq, args::three>)->RangeMultiplier(64)->Range(64, 64 << 10);
BENCHMARK(perm_vbool_check<equality::eq, args::four>)->RangeMultiplier(64)->Range(64, 64 << 10);
BENCHMARK(perm_vbool_check<equality::neq, args::three>)->RangeMultiplier(64)->Range(64, 64 << 10);
BENCHMARK(perm_vbool_check<equality::neq, args::four>)->RangeMultiplier(64)->Range(64, 64 << 10);

BENCHMARK_MAIN();
37 changes: 37 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,21 @@ namespace ranges {
} // namespace ranges
#endif // _HAS_CXX20

template <class _FwdIt1, class _FwdIt2>
_NODISCARD _CONSTEXPR20 bool _Is_permutation_of_bool(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2) {
if constexpr (!_Is_vb_iterator<_FwdIt1> && !_Is_vb_iterator<_FwdIt2>) {
auto _Pair = _STD mismatch(_First1, _Last1, _First2);
_First1 = _STD move(_Pair.first);
_First2 = _STD move(_Pair.second);

if (_First1 == _Last1) {
return true;
}
}

return _STD count(_First1, _Last1, true) == _STD count(_First2, _Last2, true);
}

_EXPORT_STD template <class _FwdIt1, class _FwdIt2, class _Pr>
_NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Pr _Pred) {
// test if [_First1, _Last1) == permuted [_First2, ...)
Expand All @@ -1260,6 +1275,18 @@ _NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _Fw
const auto _ULast1 = _STD _Get_unwrapped(_Last1);
auto _UFirst2 = _STD _Get_unwrapped_n(_First2, _STD _Idl_distance<_FwdIt1>(_UFirst1, _ULast1));

if constexpr (is_same_v<_Iter_value_t<decltype(_UFirst1)>, bool>
&& is_same_v<_Iter_value_t<decltype(_UFirst2)>, bool> //
&& _Is_ranges_random_iter_v<decltype(_UFirst1)> //
&& _Is_ranges_random_iter_v<decltype(_UFirst2)>
&& _Is_any_of_v<_Pr,
#if _HAS_CXX20
_RANGES equal_to,
#endif
equal_to<>, equal_to<bool>>) {
return _STD _Is_permutation_of_bool(_UFirst1, _ULast1, _UFirst2, _UFirst2 + (_ULast1 - _UFirst1));
}

for (;; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix
if (_UFirst1 == _ULast1) { // everything matched
return true;
Expand Down Expand Up @@ -1297,6 +1324,16 @@ _NODISCARD _CONSTEXPR20 bool is_permutation(
return false;
}

if constexpr (is_same_v<_Iter_value_t<decltype(_UFirst1)>, bool>
&& is_same_v<_Iter_value_t<decltype(_UFirst2)>, bool>
&& _Is_any_of_v<_Pr,
#if _HAS_CXX20
_RANGES equal_to,
#endif
equal_to<>, equal_to<bool>>) {
return _STD _Is_permutation_of_bool(_UFirst1, _ULast1, _UFirst2, _ULast2);
}

for (; _UFirst1 != _ULast1; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix
if (!_Pred(*_UFirst1, *_UFirst2)) {
// found first inequality, check match counts in suffix
Expand Down
45 changes: 45 additions & 0 deletions tests/std/tests/GH_000625_vector_bool_optimization/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,49 @@ CONSTEXPR20 bool test_copy_part_2() {
return true;
}

CONSTEXPR20 bool test_is_permutation() {
constexpr bool perm_source_raw[] = {
false, true, false, false, false, true, false, true, true, false, false, true, false, false, true, true};
const vector<bool> perm_source(begin(perm_source_raw), end(perm_source_raw));

// Actually a permutation
constexpr bool perm_raw[] = {
true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true};
const vector<bool> perm(begin(perm_raw), end(perm_raw));
assert(is_permutation(perm_source.begin(), perm_source.end(), perm.begin()));
assert(is_permutation(perm_source.begin(), perm_source.end(), perm.begin(), perm.end()));
assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), perm.begin()));
assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), perm.begin(), perm.end()));
assert(is_permutation(perm_source.begin(), perm_source.end(), begin(perm_raw)));
assert(is_permutation(perm_source.begin(), perm_source.end(), begin(perm_raw), end(perm_raw)));
assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(perm_raw)));
assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(perm_raw), end(perm_raw)));

// One extra true value
constexpr bool extra_true_raw[] = {
true, false, true, false, false, true, false, false, true, true, true, false, true, false, false, true};
const vector<bool> extra_true(begin(extra_true_raw), end(extra_true_raw));
assert(!is_permutation(perm_source.begin(), perm_source.end(), extra_true.begin()));
assert(!is_permutation(perm_source.begin(), perm_source.end(), extra_true.begin(), extra_true.end()));
assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), extra_true.begin()));
assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), extra_true.begin(), extra_true.end()));
assert(!is_permutation(perm_source.begin(), perm_source.end(), begin(extra_true_raw)));
assert(!is_permutation(perm_source.begin(), perm_source.end(), begin(extra_true_raw), end(extra_true_raw)));
assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(extra_true_raw)));
assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(extra_true_raw), end(extra_true_raw)));

// One element longer
constexpr bool longer_raw[] = {
true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true, false};
const vector<bool> longer(begin(longer_raw), end(longer_raw));
assert(!is_permutation(perm_source.begin(), perm_source.end(), longer.begin(), longer.end()));
assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), longer.begin(), longer.end()));
assert(!is_permutation(perm_source.begin(), perm_source.end(), begin(longer_raw), end(longer_raw)));
assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(longer_raw), end(longer_raw)));

return true;
}

void initialize_randomness(mt19937_64& gen) {
constexpr size_t n = mt19937_64::state_size;
constexpr size_t w = mt19937_64::word_size;
Expand Down Expand Up @@ -1710,6 +1753,7 @@ static_assert(test_meow_of());
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-2574489
static_assert(test_copy_part_1());
static_assert(test_copy_part_2());
static_assert(test_is_permutation());
#endif // ^^^ no workaround ^^^
#endif // _HAS_CXX20

Expand All @@ -1721,6 +1765,7 @@ int main() {
test_meow_of();
test_copy_part_1();
test_copy_part_2();
test_is_permutation();

test_huge_vector_bool();

Expand Down