Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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 @@ -141,5 +141,6 @@ add_benchmark(unique src/unique.cpp)
add_benchmark(vector_bool_copy src/vector_bool_copy.cpp)
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_transform src/vector_bool_transform.cpp)
54 changes: 54 additions & 0 deletions benchmarks/src/vector_bool_meow_of.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

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

#include "skewed_allocator.hpp"

using namespace std;

enum class alg { any_, all_, none_ };
enum class content { ones_then_zeros, zeros_then_ones };

template <alg Alg, content Content, class Pred = identity>
void meow_of(benchmark::State& state) {
const auto size = static_cast<size_t>(state.range(0));
vector<bool, not_highly_aligned_allocator<bool>> source(size);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not currently vectorized, so not_highly_aligned_allocator currently serves no purpose, as not-overaligning only matters for vectorization, but:

  • It can be vectorized manually in the future
  • gcc with -O3 would vectorize this, so the target compilers someday also might


if constexpr (Content == content::ones_then_zeros) {
fill(source.begin(), source.begin() + source.size() / 2, true);
} else {
fill(source.begin() + source.size() / 2, source.end(), true);
}

for (auto _ : state) {
benchmark::DoNotOptimize(source);
bool result;
if constexpr (Alg == alg::any_) {
result = any_of(source.begin(), source.end(), Pred{});
} else if constexpr (Alg == alg::all_) {
result = all_of(source.begin(), source.end(), Pred{});
} else {
result = none_of(source.begin(), source.end(), Pred{});
}
benchmark::DoNotOptimize(result);
}
}

void common_args(auto bm) {
bm->RangeMultiplier(64)->Range(64, 64 << 10);
}

using not_ = logical_not<>;

BENCHMARK(meow_of<alg::any_, content::zeros_then_ones>)->Apply(common_args);
BENCHMARK(meow_of<alg::any_, content::ones_then_zeros, not_>)->Apply(common_args);
BENCHMARK(meow_of<alg::all_, content::ones_then_zeros>)->Apply(common_args);
BENCHMARK(meow_of<alg::none_, content::zeros_then_ones>)->Apply(common_args);

BENCHMARK_MAIN();
51 changes: 36 additions & 15 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -1506,18 +1506,30 @@ namespace ranges {
} // namespace ranges
#endif // _HAS_CXX20

struct _All_of_vbool_traits;
struct _Any_of_vbool_traits;
struct _None_of_vbool_traits;

template <class _Traits, class _VbIt, class _Mapped_fn>
_NODISCARD _CONSTEXPR20 bool _Meow_of_vbool(_VbIt _First, _VbIt _Last, _Mapped_fn _Mapped_func);

_EXPORT_STD template <class _InIt, class _Pr>
_NODISCARD _CONSTEXPR20 bool all_of(_InIt _First, _InIt _Last, _Pr _Pred) { // test if all elements satisfy _Pred
_STD _Adl_verify_range(_First, _Last);
auto _UFirst = _STD _Get_unwrapped(_First);
const auto _ULast = _STD _Get_unwrapped(_Last);
for (; _UFirst != _ULast; ++_UFirst) {
if (!_Pred(*_UFirst)) {
return false;

if constexpr (_Is_vb_iterator<decltype(_UFirst)> && !is_void_v<_Map_vb_functor_t<_Pr>>) {
return _Meow_of_vbool<_All_of_vbool_traits>(_UFirst, _ULast, _Map_vb_functor_t<_Pr>{});
} else {
for (; _UFirst != _ULast; ++_UFirst) {
if (!_Pred(*_UFirst)) {
return false;
}
}
}

return true;
return true;
}
}

#if _HAS_CXX17
Expand Down Expand Up @@ -1573,13 +1585,18 @@ _NODISCARD _CONSTEXPR20 bool any_of(const _InIt _First, const _InIt _Last, _Pr _
_STD _Adl_verify_range(_First, _Last);
auto _UFirst = _STD _Get_unwrapped(_First);
const auto _ULast = _STD _Get_unwrapped(_Last);
for (; _UFirst != _ULast; ++_UFirst) {
if (_Pred(*_UFirst)) {
return true;

if constexpr (_Is_vb_iterator<decltype(_UFirst)> && !is_void_v<_Map_vb_functor_t<_Pr>>) {
return _Meow_of_vbool<_Any_of_vbool_traits>(_UFirst, _ULast, _Map_vb_functor_t<_Pr>{});
} else {
for (; _UFirst != _ULast; ++_UFirst) {
if (_Pred(*_UFirst)) {
return true;
}
}
}

return false;
return false;
}
}

#if _HAS_CXX17
Expand Down Expand Up @@ -1635,13 +1652,17 @@ _NODISCARD _CONSTEXPR20 bool none_of(const _InIt _First, const _InIt _Last, _Pr
_STD _Adl_verify_range(_First, _Last);
auto _UFirst = _STD _Get_unwrapped(_First);
const auto _ULast = _STD _Get_unwrapped(_Last);
for (; _UFirst != _ULast; ++_UFirst) {
if (_Pred(*_UFirst)) {
return false;
if constexpr (_Is_vb_iterator<decltype(_UFirst)> && !is_void_v<_Map_vb_functor_t<_Pr>>) {
return _Meow_of_vbool<_None_of_vbool_traits>(_UFirst, _ULast, _Map_vb_functor_t<_Pr>{});
} else {
for (; _UFirst != _ULast; ++_UFirst) {
if (_Pred(*_UFirst)) {
return false;
}
}
}

return true;
return true;
}
}

#if _HAS_CXX17
Expand Down
85 changes: 85 additions & 0 deletions stl/inc/vector
Original file line number Diff line number Diff line change
Expand Up @@ -4047,6 +4047,91 @@ _CONSTEXPR20 _OutIt _Transform_vbool_aligned(
return _Dest;
}

struct _All_of_vbool_traits {
static constexpr bool _Default_result = true;

static _CONSTEXPR20 bool _Check(const _Vbase _Value) {
return _Value != ~_Vbase{0};
}

static _CONSTEXPR20 bool _Check(const _Vbase _Value, const _Vbase _Mask) {
return (_Value & _Mask) != _Mask;
}
};

struct _Any_of_vbool_traits_base {
static _CONSTEXPR20 bool _Check(const _Vbase _Value) {
return _Value != 0;
}

static _CONSTEXPR20 bool _Check(const _Vbase _Value, const _Vbase _Mask) {
return (_Value & _Mask) != 0;
}
};

struct _Any_of_vbool_traits : _Any_of_vbool_traits_base {
static constexpr bool _Default_result = false;
};

struct _None_of_vbool_traits : _Any_of_vbool_traits_base {
static constexpr bool _Default_result = true;
};

template <class _Traits, class _VbIt, class _Mapped_fn>
_NODISCARD _CONSTEXPR20 bool _Meow_of_vbool(const _VbIt _First, const _VbIt _Last, const _Mapped_fn _Mapped_func) {
constexpr bool _Early_result = !_Traits::_Default_result;
auto _First_ptr = _First._Myptr;
const auto _Last_ptr = _Last._Myptr;

if (_First_ptr == _Last_ptr) {
const _Vbase _Mask = (_Vbase{1} << _Last._Myoff) - (_Vbase{1} << _First._Myoff);
if (_Mask == 0) {
return _Traits::_Default_result;
} else {
return _Traits::_Check(_Mapped_func(*_First_ptr), _Mask) ? _Early_result : _Traits::_Default_result;
}
}

if (_First._Myoff != 0) {
const _Vbase _Mask = static_cast<_Vbase>(-1) << _First._Myoff;
if (_Traits::_Check(_Mapped_func(*_First_ptr), _Mask)) {
return _Early_result;
}

++_First_ptr;
}

for (; _First_ptr != _Last_ptr; ++_First_ptr) {
if (_Traits::_Check(_Mapped_func(*_First_ptr))) {
return _Early_result;
}
}

if (_Last._Myoff != 0) {
const _Vbase _Mask = (_Vbase{1} << _Last._Myoff) - 1;
if (_Traits::_Check(_Mapped_func(*_First_ptr), _Mask)) {
return _Early_result;
}
}

return _Traits::_Default_result;
}

template <class _VbIt, class _Mapped_fn>
_NODISCARD _CONSTEXPR20 bool _All_of_vbool(const _VbIt _First, const _VbIt _Last, const _Mapped_fn _Mapped_func) {
return _Meow_of_vbool<_All_of_vbool_traits>(_First, _Last, _Mapped_func);
}

template <class _VbIt, class _Mapped_fn>
_NODISCARD _CONSTEXPR20 bool _Any_of_vbool(const _VbIt _First, const _VbIt _Last, const _Mapped_fn _Mapped_func) {
return _Meow_of_vbool<_Any_of_vbool_traits>(_First, _Last, _Mapped_func);
}

template <class _VbIt, class _Mapped_fn>
_NODISCARD _CONSTEXPR20 bool _None_of_vbool(const _VbIt _First, const _VbIt _Last, const _Mapped_fn _Mapped_func) {
return _Meow_of_vbool<_None_of_vbool_traits>(_First, _Last, _Mapped_func);
}

#undef _ASAN_VECTOR_MODIFY
#undef _ASAN_VECTOR_REMOVE
#undef _ASAN_VECTOR_CREATE
Expand Down
7 changes: 7 additions & 0 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4873,6 +4873,13 @@ struct _Map_vb_functor {
using type = void;
};

#if _HAS_CXX20
template <>
struct _Map_vb_functor<identity> {
using type = identity;
};
#endif // _HAS_CXX20

template <class _Fn>
using _Map_vb_functor_t = typename _Map_vb_functor<_Fn>::type;

Expand Down
100 changes: 100 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 @@ -190,6 +190,104 @@ CONSTEXPR20 bool test_transform() {
return true;
}

CONSTEXPR20 bool test__meow_of_helper(const size_t length_before, const size_t length, const size_t length_after) {
const size_t total_length = length_before + length + length_after;

vector<bool> zeros(total_length);
vector<bool> ones(total_length);
vector<bool> mix(total_length);
fill(zeros.begin(), zeros.begin() + length_before, true);
fill(zeros.end() - length_after, zeros.end(), true);
fill(ones.begin() + length_before, ones.end() - length_after, true);
fill(mix.begin(), mix.begin() + length_before, true);
fill(mix.begin() + length_before + length / 2, mix.end() - length_after, true);

const auto first_0 = zeros.begin() + length_before;
const auto last_0 = zeros.end() - length_after;
const auto first_1 = ones.cbegin() + length_before;
const auto last_1 = ones.cend() - length_after;
const auto first_m = mix.cbegin() + length_before;
const auto last_m = mix.cend() - length_after;

if (length == 0) {
#if _HAS_CXX20
assert(all_of(first_0, last_0, identity{}) == true);
assert(all_of(first_1, last_1, identity{}) == true);
assert(all_of(first_m, last_m, identity{}) == true);

assert(any_of(first_0, last_0, identity{}) == false);
assert(any_of(first_1, last_1, identity{}) == false);
assert(any_of(first_m, last_m, identity{}) == false);

assert(none_of(first_0, last_0, identity{}) == true);
assert(none_of(first_1, last_1, identity{}) == true);
assert(none_of(first_m, last_m, identity{}) == true);
#endif // _HAS_CXX20

assert(all_of(first_0, last_0, logical_not<>{}) == true);
assert(all_of(first_1, last_1, logical_not<>{}) == true);
assert(all_of(first_m, last_m, logical_not<>{}) == true);

assert(any_of(first_0, last_0, logical_not<>{}) == false);
assert(any_of(first_1, last_1, logical_not<>{}) == false);
assert(any_of(first_m, last_m, logical_not<>{}) == false);

assert(none_of(first_0, last_0, logical_not<>{}) == true);
assert(none_of(first_1, last_1, logical_not<>{}) == true);
assert(none_of(first_m, last_m, logical_not<>{}) == true);
} else {
#if _HAS_CXX20
assert(all_of(first_0, last_0, identity{}) == false);
assert(all_of(first_1, last_1, identity{}) == true);
assert(all_of(first_m, last_m, identity{}) == false);

assert(any_of(first_0, last_0, identity{}) == false);
assert(any_of(first_1, last_1, identity{}) == true);
assert(any_of(first_m, last_m, identity{}) == true);

assert(none_of(first_0, last_0, identity{}) == true);
assert(none_of(first_1, last_1, identity{}) == false);
assert(none_of(first_m, last_m, identity{}) == false);
#endif // _HAS_CXX20

assert(all_of(first_0, last_0, logical_not<>{}) == true);
assert(all_of(first_1, last_1, logical_not<>{}) == false);
assert(all_of(first_m, last_m, logical_not<>{}) == false);

assert(any_of(first_0, last_0, logical_not<>{}) == true);
assert(any_of(first_1, last_1, logical_not<>{}) == false);
assert(any_of(first_m, last_m, logical_not<>{}) == true);

assert(none_of(first_0, last_0, logical_not<>{}) == false);
assert(none_of(first_1, last_1, logical_not<>{}) == true);
assert(none_of(first_m, last_m, logical_not<>{}) == false);
}

return true;
}

CONSTEXPR20 bool test_meow_of() {
// Empty range
test__meow_of_helper(0, 0, 3);
test__meow_of_helper(3, 0, 3);

// One block, ends within block
test__meow_of_helper(0, 10, 3);
test__meow_of_helper(3, 10, 3);

// One block, exactly
test__meow_of_helper(0, blockSize, 0);

// Multiple blocks, spanning
test__meow_of_helper(3, blockSize - 2, 3);
test__meow_of_helper(3, blockSize + 2, 3);

// Many blocks, exaclty
test__meow_of_helper(blockSize, 4 * blockSize, blockSize);

return true;
}

CONSTEXPR20 void test_fill_helper(const size_t length) {
// No offset
{
Expand Down Expand Up @@ -1531,6 +1629,7 @@ static_assert(test_fill());
static_assert(test_find());
static_assert(test_count());
static_assert(test_transform());
static_assert(test_meow_of());

#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-2574489
static_assert(test_copy_part_1());
Expand All @@ -1543,6 +1642,7 @@ int main() {
test_find();
test_count();
test_transform();
test_meow_of();
test_copy_part_1();
test_copy_part_2();

Expand Down
Loading