forked from OSchip/llvm-project
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
This commit is contained in:
parent
efc3d1dfaa
commit
06a9b5a305
|
@ -1,62 +1,270 @@
|
|||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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 <class GenInputs>
|
||||
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<ValueType> 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<AllValueTypes, ValueType, 2> {
|
||||
static constexpr const char* Names[] = {"uint32", "string"};
|
||||
};
|
||||
|
||||
template <class V>
|
||||
using Value =
|
||||
std::conditional_t<V() == ValueType::Uint32, uint32_t, std::string>;
|
||||
|
||||
enum class Order {
|
||||
Random,
|
||||
Ascending,
|
||||
Descending,
|
||||
SingleElement,
|
||||
PipeOrgan,
|
||||
Heap
|
||||
};
|
||||
struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 6> {
|
||||
static constexpr const char* Names[] = {"Random", "Ascending",
|
||||
"Descending", "SingleElement",
|
||||
"PipeOrgan", "Heap"};
|
||||
};
|
||||
|
||||
void fillValues(std::vector<uint32_t>& 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<uint32_t>)->Arg(TestNumInputs);
|
||||
void fillValues(std::vector<std::string>& V, size_t N, Order O) {
|
||||
|
||||
BENCHMARK_CAPTURE(BM_Sort, sorted_ascending_uint32,
|
||||
getSortedIntegerInputs<uint32_t>)->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<uint32_t>)->Arg(TestNumInputs);
|
||||
template <class T>
|
||||
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<uint32_t>)->Arg(TestNumInputs);
|
||||
template <class ValueType>
|
||||
std::vector<std::vector<Value<ValueType> > > 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::pair<size_t, Order>, std::vector<Value<ValueType> > >
|
||||
Cached;
|
||||
|
||||
BENCHMARK_CAPTURE(BM_Sort, pipe_organ_uint32,
|
||||
getPipeOrganIntegerInputs<uint32_t>)->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 <class T, class U>
|
||||
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 <class ValueType, class F>
|
||||
void runOpOnCopies(benchmark::State& state, size_t Quantity, Order O,
|
||||
bool CountElements, F f) {
|
||||
auto Copies = makeOrderedValues<ValueType>(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 <class ValueType, class Order>
|
||||
struct Sort {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(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 <class ValueType, class Order>
|
||||
struct StableSort {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(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 <class ValueType, class Order>
|
||||
struct MakeHeap {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(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 <class ValueType>
|
||||
struct SortHeap {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(
|
||||
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 <class ValueType, class Order>
|
||||
struct MakeThenSortHeap {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(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 <class ValueType, class Order>
|
||||
struct PushHeap {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(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 <class ValueType>
|
||||
struct PopHeap {
|
||||
size_t Quantity;
|
||||
|
||||
void run(benchmark::State& state) const {
|
||||
runOpOnCopies<ValueType>(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<size_t> Quantities = {1 << 0, 1 << 2, 1 << 4, 1 << 6,
|
||||
1 << 8, 1 << 10, 1 << 14, 1 << 18};
|
||||
makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
|
||||
makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(
|
||||
Quantities);
|
||||
makeCartesianProductBenchmark<MakeHeap, AllValueTypes, AllOrders>(Quantities);
|
||||
makeCartesianProductBenchmark<SortHeap, AllValueTypes>(Quantities);
|
||||
makeCartesianProductBenchmark<MakeThenSortHeap, AllValueTypes, AllOrders>(
|
||||
Quantities);
|
||||
makeCartesianProductBenchmark<PushHeap, AllValueTypes, AllOrders>(Quantities);
|
||||
makeCartesianProductBenchmark<PopHeap, AllValueTypes>(Quantities);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue