From fd1d9116c36c264bc62f5771c2982a249826cca8 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 24 May 2025 23:31:06 +0300 Subject: [PATCH 01/10] benchmark --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/includes.cpp | 91 +++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 benchmarks/src/includes.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 719d7deec21..043967488f2 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -111,6 +111,7 @@ add_benchmark(fill src/fill.cpp) add_benchmark(find_and_count src/find_and_count.cpp) add_benchmark(find_first_of src/find_first_of.cpp) add_benchmark(has_single_bit src/has_single_bit.cpp) +add_benchmark(includes src/includes.cpp) add_benchmark(iota src/iota.cpp) add_benchmark(is_sorted_until src/is_sorted_until.cpp) add_benchmark(locale_classic src/locale_classic.cpp) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp new file mode 100644 index 00000000000..652b22222c9 --- /dev/null +++ b/benchmarks/src/includes.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include "skewed_allocator.hpp" +#include "utility.hpp" + +using namespace std; + +enum class alg_type { std_fn, rng }; + +enum needle_spread { dense, sparse, random_pick }; + +template +void bm_includes(benchmark::State& state) { + const auto hay_size = static_cast(state.range(0)); + const auto needle_size = static_cast(state.range(1)); + const auto s = static_cast(state.range(2)); + const auto m = static_cast(state.range(3)); + + auto hay = random_vector(hay_size); + ranges::sort(hay); + + vector> needle(needle_size); + switch (s) { + case needle_spread::dense: + needle.assign(hay.begin() + hay_size / 2 - needle_size / 2, hay.begin() + hay_size / 2 + (needle_size + 1) / 2); + break; + + case needle_spread::sparse: + for (size_t i = 0; i != needle_size; ++i) { + needle[i] = hay[hay_size * i / needle_size + hay_size / (needle_size * 2)]; + } + break; + + case needle_spread::random_pick: + ranges::sample(hay, needle.begin(), needle_size, std::mt19937{}); + break; + } + + if (!m) { + const T v = needle[needle_size / 2]; + const T r = static_cast(static_cast>(v + 1)); + ranges::replace(hay, v, r); + } + + for (auto _ : state) { + benchmark::DoNotOptimize(hay); + benchmark::DoNotOptimize(needle); + bool found; + if constexpr (Alg == alg_type::rng) { + found = ranges::includes(hay, needle); + } else { + found = includes(hay.begin(), hay.end(), needle.begin(), needle.end()); + } + benchmark::DoNotOptimize(found); + if (found != m) { + cerr << "Unexpected 'includes' result: " << found << '\n'; + abort(); + } + } +} + +void common_args(auto bm) { + for (const auto& hay_size : {300, 3000}) { + for (const auto& needle_size : {3, 22, 105, 290}) { + for (const auto& s : {needle_spread::dense, needle_spread::sparse, needle_spread::random_pick}) { + for (const auto& m : {true, false}) { + bm->Args({hay_size, needle_size, s, m}); + } + } + } + } +} + +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); + +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); +BENCHMARK(bm_includes)->Apply(common_args); + +BENCHMARK_MAIN(); From 8199c12606051152e3332f86c2f1b742a4132f32 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 24 May 2025 23:44:30 +0300 Subject: [PATCH 02/10] speedup! --- stl/inc/algorithm | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 45799e37a3a..af4c82ce46b 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10158,17 +10158,31 @@ _NODISCARD _CONSTEXPR20 bool includes(_InIt1 _First1, _InIt1 _Last1, _InIt2 _Fir const auto _ULast2 = _STD _Get_unwrapped(_Last2); _DEBUG_ORDER_SET_UNWRAPPED(_InIt2, _UFirst1, _ULast1, _Pred); _DEBUG_ORDER_SET_UNWRAPPED(_InIt1, _UFirst2, _ULast2, _Pred); - for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1) { - if (_DEBUG_LT_PRED(_Pred, *_UFirst2, *_UFirst1)) { - return false; - } - if (!_Pred(*_UFirst1, *_UFirst2)) { + if (_UFirst2 == _ULast2) { + return true; + } else if (_UFirst1 == _ULast1) { + return false; + } + + for (;;) { + if (_DEBUG_LT_PRED(_Pred, *_UFirst1, *_UFirst2)) { + ++_UFirst1; + if (_UFirst1 == _ULast1) { + return false; + } + } else if (_Pred(*_UFirst2, *_UFirst1)) { + return false; + } else { + ++_UFirst1; ++_UFirst2; + if (_UFirst2 == _ULast2) { + return true; + } else if (_UFirst1 == _ULast1) { + return false; + } } } - - return _UFirst2 == _ULast2; } _EXPORT_STD template From 01da45fe629504abbf36922310c9eb037d04432b Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 26 May 2025 21:46:02 +0300 Subject: [PATCH 03/10] improve benchmark --- benchmarks/src/includes.cpp | 43 +++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index 652b22222c9..e034dbec862 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -14,7 +14,7 @@ using namespace std; enum class alg_type { std_fn, rng }; -enum needle_spread { dense, sparse, random_pick }; +enum needle_spread { dense, dense_random, sparse, sparse_random }; template void bm_includes(benchmark::State& state) { @@ -32,14 +32,36 @@ void bm_includes(benchmark::State& state) { needle.assign(hay.begin() + hay_size / 2 - needle_size / 2, hay.begin() + hay_size / 2 + (needle_size + 1) / 2); break; + case needle_spread::dense_random: + { + mt19937 gen{}; + geometric_distribution dis_dis{}; + vector idx(needle_size); + const size_t mid = needle_size / 2; + idx[mid] = hay_size / 2; + + const size_t max_shift = hay_size / needle_size; + + for (size_t i = mid; i != 0; --i) { + idx[i - 1] = idx[i] - min(dis_dis(gen) + 1, max_shift); + } + + for (size_t i = mid; i != needle_size - 1; ++i) { + idx[i + 1] = idx[i] + min(dis_dis(gen) + 1, max_shift); + } + + transform(idx.begin(), idx.end(), needle.begin(), [&hay](const size_t i) { return hay[i]; }); + } + break; + case needle_spread::sparse: for (size_t i = 0; i != needle_size; ++i) { needle[i] = hay[hay_size * i / needle_size + hay_size / (needle_size * 2)]; } break; - case needle_spread::random_pick: - ranges::sample(hay, needle.begin(), needle_size, std::mt19937{}); + case needle_spread::sparse_random: + ranges::sample(hay, needle.begin(), needle_size, mt19937{}); break; } @@ -67,12 +89,15 @@ void bm_includes(benchmark::State& state) { } void common_args(auto bm) { - for (const auto& hay_size : {300, 3000}) { - for (const auto& needle_size : {3, 22, 105, 290}) { - for (const auto& s : {needle_spread::dense, needle_spread::sparse, needle_spread::random_pick}) { - for (const auto& m : {true, false}) { - bm->Args({hay_size, needle_size, s, m}); - } + for (const auto& s : + {needle_spread::dense, needle_spread::dense_random, needle_spread::sparse, needle_spread::sparse_random}) { + for (const auto& m : {true, false}) { + for (const auto& needle_size : {3, 22, 105, 1504, 2750}) { + bm->Args({3000, needle_size, s, m}); + } + + for (const auto& needle_size : {3, 22, 105, 290}) { + bm->Args({300, needle_size, s, m}); } } } From 556fb6574f96476ce7acd3ccd5ae50eb024f20f7 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 1 Jun 2025 14:31:12 +0300 Subject: [PATCH 04/10] modernize --- benchmarks/src/includes.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index e034dbec862..367f9bbbaba 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "skewed_allocator.hpp" @@ -26,7 +27,7 @@ void bm_includes(benchmark::State& state) { auto hay = random_vector(hay_size); ranges::sort(hay); - vector> needle(needle_size); + vector> needle; switch (s) { case needle_spread::dense: needle.assign(hay.begin() + hay_size / 2 - needle_size / 2, hay.begin() + hay_size / 2 + (needle_size + 1) / 2); @@ -50,17 +51,19 @@ void bm_includes(benchmark::State& state) { idx[i + 1] = idx[i] + min(dis_dis(gen) + 1, max_shift); } - transform(idx.begin(), idx.end(), needle.begin(), [&hay](const size_t i) { return hay[i]; }); + needle.assign_range(idx | views::transform([&hay](const size_t i) { return hay[i]; })); } break; case needle_spread::sparse: + needle.resize(needle_size); for (size_t i = 0; i != needle_size; ++i) { needle[i] = hay[hay_size * i / needle_size + hay_size / (needle_size * 2)]; } break; case needle_spread::sparse_random: + needle.resize(needle_size); ranges::sample(hay, needle.begin(), needle_size, mt19937{}); break; } From c7d4c44b95bb5205ff0255cf36a47a28971953d3 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Jun 2025 12:51:33 -0700 Subject: [PATCH 05/10] `enum needle_spread` => `enum class needle_spread` --- benchmarks/src/includes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index 367f9bbbaba..7493a58bbae 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -15,7 +15,7 @@ using namespace std; enum class alg_type { std_fn, rng }; -enum needle_spread { dense, dense_random, sparse, sparse_random }; +enum class needle_spread { dense, dense_random, sparse, sparse_random }; template void bm_includes(benchmark::State& state) { From 02fb75f5bba4a0e227ad4f1f8f07b2e1c8c8033b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Jun 2025 12:59:19 -0700 Subject: [PATCH 06/10] Include more headers. --- benchmarks/src/includes.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index 7493a58bbae..62065bb3edc 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -3,9 +3,13 @@ #include #include +#include #include +#include #include +#include #include +#include #include #include "skewed_allocator.hpp" From 316fbd92674a727889ace084f7a2d1141cf4712f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Jun 2025 13:08:54 -0700 Subject: [PATCH 07/10] `s` => `spread`, `m` => `expected_match` --- benchmarks/src/includes.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index 62065bb3edc..4b7f7c92ae1 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -23,16 +23,16 @@ enum class needle_spread { dense, dense_random, sparse, sparse_random }; template void bm_includes(benchmark::State& state) { - const auto hay_size = static_cast(state.range(0)); - const auto needle_size = static_cast(state.range(1)); - const auto s = static_cast(state.range(2)); - const auto m = static_cast(state.range(3)); + const auto hay_size = static_cast(state.range(0)); + const auto needle_size = static_cast(state.range(1)); + const auto spread = static_cast(state.range(2)); + const auto expected_match = static_cast(state.range(3)); auto hay = random_vector(hay_size); ranges::sort(hay); vector> needle; - switch (s) { + switch (spread) { case needle_spread::dense: needle.assign(hay.begin() + hay_size / 2 - needle_size / 2, hay.begin() + hay_size / 2 + (needle_size + 1) / 2); break; @@ -72,7 +72,7 @@ void bm_includes(benchmark::State& state) { break; } - if (!m) { + if (!expected_match) { const T v = needle[needle_size / 2]; const T r = static_cast(static_cast>(v + 1)); ranges::replace(hay, v, r); @@ -88,7 +88,7 @@ void bm_includes(benchmark::State& state) { found = includes(hay.begin(), hay.end(), needle.begin(), needle.end()); } benchmark::DoNotOptimize(found); - if (found != m) { + if (found != expected_match) { cerr << "Unexpected 'includes' result: " << found << '\n'; abort(); } @@ -96,15 +96,15 @@ void bm_includes(benchmark::State& state) { } void common_args(auto bm) { - for (const auto& s : + for (const auto& spread : {needle_spread::dense, needle_spread::dense_random, needle_spread::sparse, needle_spread::sparse_random}) { - for (const auto& m : {true, false}) { + for (const auto& expected_match : {true, false}) { for (const auto& needle_size : {3, 22, 105, 1504, 2750}) { - bm->Args({3000, needle_size, s, m}); + bm->Args({3000, needle_size, spread, expected_match}); } for (const auto& needle_size : {3, 22, 105, 290}) { - bm->Args({300, needle_size, s, m}); + bm->Args({300, needle_size, spread, expected_match}); } } } From 8df5036de52c4dc0087798c068097e02b7d302fe Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Jun 2025 13:20:37 -0700 Subject: [PATCH 08/10] Avoid a 1-in-4-billion chance of UB. --- benchmarks/src/includes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index 4b7f7c92ae1..fa8f8d1b429 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -74,7 +74,7 @@ void bm_includes(benchmark::State& state) { if (!expected_match) { const T v = needle[needle_size / 2]; - const T r = static_cast(static_cast>(v + 1)); + const T r = static_cast(static_cast>(v) + 1); ranges::replace(hay, v, r); } From 8796e35020a8105967534d54528e3926415d8f7d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Jun 2025 14:18:33 -0700 Subject: [PATCH 09/10] Oh, that's why it was a plain enum. --- benchmarks/src/includes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index fa8f8d1b429..d9ada7568a8 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -100,11 +100,11 @@ void common_args(auto bm) { {needle_spread::dense, needle_spread::dense_random, needle_spread::sparse, needle_spread::sparse_random}) { for (const auto& expected_match : {true, false}) { for (const auto& needle_size : {3, 22, 105, 1504, 2750}) { - bm->Args({3000, needle_size, spread, expected_match}); + bm->Args({3000, needle_size, static_cast>(spread), expected_match}); } for (const auto& needle_size : {3, 22, 105, 290}) { - bm->Args({300, needle_size, spread, expected_match}); + bm->Args({300, needle_size, static_cast>(spread), expected_match}); } } } From dac8ad2705f8870141de50e4793e2c71fc0bca82 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 13 Jun 2025 05:29:52 -0700 Subject: [PATCH 10/10] Reestablish potentially broken sorted precondition. --- benchmarks/src/includes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/src/includes.cpp b/benchmarks/src/includes.cpp index d9ada7568a8..d349bde1687 100644 --- a/benchmarks/src/includes.cpp +++ b/benchmarks/src/includes.cpp @@ -76,6 +76,7 @@ void bm_includes(benchmark::State& state) { const T v = needle[needle_size / 2]; const T r = static_cast(static_cast>(v) + 1); ranges::replace(hay, v, r); + ranges::sort(hay); } for (auto _ : state) {