From 06a9b5a30557698f8eccc920533a5432a56ee8db Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen Date: Tue, 20 Nov 2018 17:15:17 +0000 Subject: [PATCH] Add benchmarks for sorting and heap functions. Summary: Benchmarks for std::sort, std::stable_sort, std::make_heap, std::sort_heap, std::pop_heap and std::push_heap. The benchmarks are run with integers and strings, and with different sorted input. Reviewers: EricWF Subscribers: christof, mgrang, ldionne, libcxx-commits Differential Revision: https://reviews.llvm.org/D53978 llvm-svn: 347329 --- libcxx/benchmarks/algorithms.bench.cpp | 298 +++++++++++++++++++++---- 1 file changed, 253 insertions(+), 45 deletions(-) diff --git a/libcxx/benchmarks/algorithms.bench.cpp b/libcxx/benchmarks/algorithms.bench.cpp index 86315390e0d2..eee8a4da2ab0 100644 --- a/libcxx/benchmarks/algorithms.bench.cpp +++ b/libcxx/benchmarks/algorithms.bench.cpp @@ -1,62 +1,270 @@ -#include -#include + +#include #include +#include +#include +#include +#include +#include -#include "benchmark/benchmark.h" +#include "CartesianBenchmarks.hpp" #include "GenerateInput.hpp" +#include "benchmark/benchmark.h" +#include "test_macros.h" -constexpr std::size_t TestNumInputs = 1024; +namespace { -template -void BM_Sort(benchmark::State& st, GenInputs gen) { - using ValueType = typename decltype(gen(0))::value_type; - const auto in = gen(st.range(0)); - std::vector inputs[5]; - auto reset_inputs = [&]() { - for (auto& C : inputs) { - C = in; - benchmark::DoNotOptimize(C.data()); - } - }; - reset_inputs(); - while (st.KeepRunning()) { - for (auto& I : inputs) { - std::sort(I.data(), I.data() + I.size()); - benchmark::DoNotOptimize(I.data()); - } - st.PauseTiming(); - reset_inputs(); - benchmark::ClobberMemory(); - st.ResumeTiming(); - } +enum class ValueType { Uint32, String }; +struct AllValueTypes : EnumValuesAsTuple { + static constexpr const char* Names[] = {"uint32", "string"}; +}; + +template +using Value = + std::conditional_t; + +enum class Order { + Random, + Ascending, + Descending, + SingleElement, + PipeOrgan, + Heap +}; +struct AllOrders : EnumValuesAsTuple { + static constexpr const char* Names[] = {"Random", "Ascending", + "Descending", "SingleElement", + "PipeOrgan", "Heap"}; +}; + +void fillValues(std::vector& V, size_t N, Order O) { + if (O == Order::SingleElement) { + V.resize(N, 0); + } else { + while (V.size() < N) + V.push_back(V.size()); + } } -BENCHMARK_CAPTURE(BM_Sort, random_uint32, - getRandomIntegerInputs)->Arg(TestNumInputs); +void fillValues(std::vector& V, size_t N, Order O) { -BENCHMARK_CAPTURE(BM_Sort, sorted_ascending_uint32, - getSortedIntegerInputs)->Arg(TestNumInputs); + if (O == Order::SingleElement) { + V.resize(N, getRandomString(1024)); + } else { + while (V.size() < N) + V.push_back(getRandomString(1024)); + } +} -BENCHMARK_CAPTURE(BM_Sort, sorted_descending_uint32, - getReverseSortedIntegerInputs)->Arg(TestNumInputs); +template +void sortValues(T& V, Order O) { + assert(std::is_sorted(V.begin(), V.end())); + switch (O) { + case Order::Random: { + std::random_device R; + std::mt19937 M(R()); + std::shuffle(V.begin(), V.end(), M); + break; + } + case Order::Ascending: + std::sort(V.begin(), V.end()); + break; + case Order::Descending: + std::sort(V.begin(), V.end(), std::greater<>()); + break; + case Order::SingleElement: + // Nothing to do + break; + case Order::PipeOrgan: + std::sort(V.begin(), V.end()); + std::reverse(V.begin() + V.size() / 2, V.end()); + break; + case Order::Heap: + std::make_heap(V.begin(), V.end()); + break; + } +} -BENCHMARK_CAPTURE(BM_Sort, single_element_uint32, - getDuplicateIntegerInputs)->Arg(TestNumInputs); +template +std::vector > > makeOrderedValues(size_t N, + Order O) { + // Let's make sure that all random sequences of the same size are the same. + // That way we can compare the different algorithms with the same input. + static std::map, std::vector > > + Cached; -BENCHMARK_CAPTURE(BM_Sort, pipe_organ_uint32, - getPipeOrganIntegerInputs)->Arg(TestNumInputs); + auto& Values = Cached[{N, O}]; + if (Values.empty()) { + fillValues(Values, N, O); + sortValues(Values, O); + }; + const size_t NumCopies = std::max(size_t{1}, 1000 / N); + return { NumCopies, Values }; +} -BENCHMARK_CAPTURE(BM_Sort, random_strings, - getRandomStringInputs)->Arg(TestNumInputs); +template +TEST_ALWAYS_INLINE void resetCopies(benchmark::State& state, T& Copies, + U& Orig) { + state.PauseTiming(); + for (auto& Copy : Copies) + Copy = Orig; + state.ResumeTiming(); +} -BENCHMARK_CAPTURE(BM_Sort, sorted_ascending_strings, - getSortedStringInputs)->Arg(TestNumInputs); +template +void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O, + bool CountElements, F f) { + auto Copies = makeOrderedValues(Quantity, O); + const auto Orig = Copies[0]; -BENCHMARK_CAPTURE(BM_Sort, sorted_descending_strings, - getReverseSortedStringInputs)->Arg(TestNumInputs); + const size_t Batch = CountElements ? Copies.size() * Quantity : Copies.size(); + while (state.KeepRunningBatch(Batch)) { + for (auto& Copy : Copies) { + f(Copy); + benchmark::DoNotOptimize(Copy); + } + resetCopies(state, Copies, Orig); + } +} -BENCHMARK_CAPTURE(BM_Sort, single_element_strings, - getDuplicateStringInputs)->Arg(TestNumInputs); +template +struct Sort { + size_t Quantity; + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { + std::sort(Copy.begin(), Copy.end()); + }); + } -BENCHMARK_MAIN(); + bool skip() const { return Order() == ::Order::Heap; } + + std::string name() const { + return "BM_Sort" + ValueType::name() + Order::name() + "_" + + std::to_string(Quantity); + }; +}; + +template +struct StableSort { + size_t Quantity; + + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { + std::stable_sort(Copy.begin(), Copy.end()); + }); + } + + bool skip() const { return Order() == ::Order::Heap; } + + std::string name() const { + return "BM_StableSort" + ValueType::name() + Order::name() + "_" + + std::to_string(Quantity); + }; +}; + +template +struct MakeHeap { + size_t Quantity; + + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { + std::make_heap(Copy.begin(), Copy.end()); + }); + } + + std::string name() const { + return "BM_MakeHeap" + ValueType::name() + Order::name() + "_" + + std::to_string(Quantity); + }; +}; + +template +struct SortHeap { + size_t Quantity; + + void run(benchmark::State& state) const { + runOpOnCopies( + state, Quantity, Order::Heap, false, + [](auto& Copy) { std::sort_heap(Copy.begin(), Copy.end()); }); + } + + std::string name() const { + return "BM_SortHeap" + ValueType::name() + "_" + std::to_string(Quantity); + }; +}; + +template +struct MakeThenSortHeap { + size_t Quantity; + + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order(), false, [](auto& Copy) { + std::make_heap(Copy.begin(), Copy.end()); + std::sort_heap(Copy.begin(), Copy.end()); + }); + } + + std::string name() const { + return "BM_MakeThenSortHeap" + ValueType::name() + Order::name() + "_" + + std::to_string(Quantity); + }; +}; + +template +struct PushHeap { + size_t Quantity; + + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order(), true, [](auto& Copy) { + for (auto I = Copy.begin(), E = Copy.end(); I != E; ++I) { + std::push_heap(Copy.begin(), I + 1); + } + }); + } + + bool skip() const { return Order() == ::Order::Heap; } + + std::string name() const { + return "BM_PushHeap" + ValueType::name() + Order::name() + "_" + + std::to_string(Quantity); + }; +}; + +template +struct PopHeap { + size_t Quantity; + + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order(), true, [](auto& Copy) { + for (auto B = Copy.begin(), I = Copy.end(); I != B; --I) { + std::pop_heap(B, I); + } + }); + } + + std::string name() const { + return "BM_PopHeap" + ValueType::name() + "_" + std::to_string(Quantity); + }; +}; + +} // namespace + +int main(int argc, char** argv) { + benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) + return 1; + + const std::vector Quantities = {1 << 0, 1 << 2, 1 << 4, 1 << 6, + 1 << 8, 1 << 10, 1 << 14, 1 << 18}; + makeCartesianProductBenchmark(Quantities); + makeCartesianProductBenchmark( + Quantities); + makeCartesianProductBenchmark(Quantities); + makeCartesianProductBenchmark(Quantities); + makeCartesianProductBenchmark( + Quantities); + makeCartesianProductBenchmark(Quantities); + makeCartesianProductBenchmark(Quantities); + benchmark::RunSpecifiedBenchmarks(); +}