|  | #include <array> | 
|  | #include <algorithm> | 
|  | #include <cassert> | 
|  | #include <cstdint> | 
|  | #include <tuple> | 
|  | #include <vector> | 
|  |  | 
|  | #include "benchmark/benchmark.h" | 
|  |  | 
|  | #include "CartesianBenchmarks.h" | 
|  | #include "GenerateInput.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <typename I, typename N> | 
|  | std::array<I, 10> every_10th_percentile_N(I first, N n) { | 
|  | N step = n / 10; | 
|  | std::array<I, 10> res; | 
|  |  | 
|  | for (size_t i = 0; i < 10; ++i) { | 
|  | res[i] = first; | 
|  | std::advance(first, step); | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | template <class IntT> | 
|  | struct TestIntBase { | 
|  | static std::vector<IntT> generateInput(size_t size) { | 
|  | std::vector<IntT> Res(size); | 
|  | std::generate(Res.begin(), Res.end(), | 
|  | [] { return getRandomInteger<IntT>(); }); | 
|  | return Res; | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct TestInt32 : TestIntBase<std::int32_t> { | 
|  | static constexpr const char* Name = "TestInt32"; | 
|  | }; | 
|  |  | 
|  | struct TestInt64 : TestIntBase<std::int64_t> { | 
|  | static constexpr const char* Name = "TestInt64"; | 
|  | }; | 
|  |  | 
|  | struct TestUint32 : TestIntBase<std::uint32_t> { | 
|  | static constexpr const char* Name = "TestUint32"; | 
|  | }; | 
|  |  | 
|  | struct TestMediumString { | 
|  | static constexpr const char* Name = "TestMediumString"; | 
|  | static constexpr size_t StringSize = 32; | 
|  |  | 
|  | static std::vector<std::string> generateInput(size_t size) { | 
|  | std::vector<std::string> Res(size); | 
|  | std::generate(Res.begin(), Res.end(), [] { return getRandomString(StringSize); }); | 
|  | return Res; | 
|  | } | 
|  | }; | 
|  |  | 
|  | using AllTestTypes = std::tuple<TestInt32, TestInt64, TestUint32, TestMediumString>; | 
|  |  | 
|  | struct LowerBoundAlg { | 
|  | template <class I, class V> | 
|  | I operator()(I first, I last, const V& value) const { | 
|  | return std::lower_bound(first, last, value); | 
|  | } | 
|  |  | 
|  | static constexpr const char* Name = "LowerBoundAlg"; | 
|  | }; | 
|  |  | 
|  | struct UpperBoundAlg { | 
|  | template <class I, class V> | 
|  | I operator()(I first, I last, const V& value) const { | 
|  | return std::upper_bound(first, last, value); | 
|  | } | 
|  |  | 
|  | static constexpr const char* Name = "UpperBoundAlg"; | 
|  | }; | 
|  |  | 
|  | struct EqualRangeAlg { | 
|  | template <class I, class V> | 
|  | std::pair<I, I> operator()(I first, I last, const V& value) const { | 
|  | return std::equal_range(first, last, value); | 
|  | } | 
|  |  | 
|  | static constexpr const char* Name = "EqualRangeAlg"; | 
|  | }; | 
|  |  | 
|  | using AllAlgs = std::tuple<LowerBoundAlg, UpperBoundAlg, EqualRangeAlg>; | 
|  |  | 
|  | template <class Alg, class TestType> | 
|  | struct PartitionPointBench { | 
|  | size_t Quantity; | 
|  |  | 
|  | std::string name() const { | 
|  | return std::string("PartitionPointBench_") + Alg::Name + "_" + | 
|  | TestType::Name + '/' + std::to_string(Quantity); | 
|  | } | 
|  |  | 
|  | void run(benchmark::State& state) const { | 
|  | auto Data = TestType::generateInput(Quantity); | 
|  | std::sort(Data.begin(), Data.end()); | 
|  | auto Every10Percentile = every_10th_percentile_N(Data.begin(), Data.size()); | 
|  |  | 
|  | for (auto _ : state) { | 
|  | for (auto Test : Every10Percentile) | 
|  | benchmark::DoNotOptimize(Alg{}(Data.begin(), Data.end(), *Test)); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | int main(int argc, char** argv) { | 
|  | benchmark::Initialize(&argc, argv); | 
|  | if (benchmark::ReportUnrecognizedArguments(argc, argv)) | 
|  | return 1; | 
|  |  | 
|  | const std::vector<size_t> Quantities = {1 << 8, 1 << 10, 1 << 20}; | 
|  | makeCartesianProductBenchmark<PartitionPointBench, AllAlgs, AllTestTypes>( | 
|  | Quantities); | 
|  | benchmark::RunSpecifiedBenchmarks(); | 
|  | } |