mirror of
https://github.com/mfocko/blog.git
synced 2024-11-10 08:19:07 +01:00
134 lines
4.1 KiB
C++
134 lines
4.1 KiB
C++
|
#include <bit>
|
||
|
#include <cassert>
|
||
|
#include <chrono>
|
||
|
#include <cstdint>
|
||
|
#include <functional>
|
||
|
#include <iostream>
|
||
|
#include <ranges>
|
||
|
#include <set>
|
||
|
#include <string>
|
||
|
#include <unordered_set>
|
||
|
|
||
|
using elem_t = std::uint64_t;
|
||
|
|
||
|
const elem_t N_ELEMENTS = 10000000;
|
||
|
#define LOOPS 10
|
||
|
|
||
|
template <typename T> struct strategy {
|
||
|
virtual std::string name() const = 0;
|
||
|
virtual T elements() = 0;
|
||
|
|
||
|
template <typename C> void run(C &&s) {
|
||
|
using namespace std;
|
||
|
|
||
|
cout << "\nBenchmarking:\t\t" << name() << '\n';
|
||
|
|
||
|
auto start = chrono::steady_clock::now();
|
||
|
for (auto x : elements()) {
|
||
|
s.insert(x);
|
||
|
}
|
||
|
auto after_insertion = chrono::steady_clock::now();
|
||
|
|
||
|
auto insertion_time =
|
||
|
chrono::duration_cast<chrono::milliseconds>(after_insertion - start);
|
||
|
cout << "Insertion phase:\t" << insertion_time << "\n";
|
||
|
|
||
|
start = chrono::steady_clock::now();
|
||
|
for (int i = 0; i < LOOPS; ++i) {
|
||
|
for (auto x : elements()) {
|
||
|
assert(s.contains(x));
|
||
|
}
|
||
|
}
|
||
|
auto after_lookups = chrono::steady_clock::now();
|
||
|
|
||
|
auto lookup_time =
|
||
|
chrono::duration_cast<chrono::milliseconds>(after_lookups - start);
|
||
|
cout << "Lookup phase:\t\t" << lookup_time << "\n";
|
||
|
}
|
||
|
|
||
|
virtual ~strategy() = default;
|
||
|
};
|
||
|
|
||
|
using iota_t =
|
||
|
decltype(std::views::iota(static_cast<elem_t>(0), static_cast<elem_t>(0)));
|
||
|
|
||
|
struct ascending_ordered_sequence : public strategy<iota_t> {
|
||
|
std::string name() const override { return "ordered sequence (ascending)"; }
|
||
|
iota_t elements() override {
|
||
|
return std::views::iota(static_cast<elem_t>(0), N_ELEMENTS);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static elem_t reverse(elem_t x) { return static_cast<elem_t>(N_ELEMENTS) - x; }
|
||
|
using reversed_iota_t =
|
||
|
decltype(std::views::iota(static_cast<elem_t>(0), static_cast<elem_t>(0)) |
|
||
|
std::views::transform(reverse));
|
||
|
|
||
|
struct descending_ordered_sequence : public strategy<reversed_iota_t> {
|
||
|
std::string name() const override { return "ordered sequence (descending)"; }
|
||
|
reversed_iota_t elements() override {
|
||
|
return std::views::iota(static_cast<elem_t>(1), N_ELEMENTS + 1) |
|
||
|
std::views::transform(reverse);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static elem_t attack(elem_t x) { return x << (5 + std::bit_width(x)); }
|
||
|
using attacked_iota_t =
|
||
|
decltype(std::views::iota(static_cast<elem_t>(0), static_cast<elem_t>(0)) |
|
||
|
std::views::transform(attack));
|
||
|
|
||
|
struct progressive_ascending_attack : public strategy<attacked_iota_t> {
|
||
|
std::string name() const override {
|
||
|
return "progressive sequence that self-heals on resize";
|
||
|
}
|
||
|
attacked_iota_t elements() override {
|
||
|
return std::views::iota(static_cast<elem_t>(0), N_ELEMENTS) |
|
||
|
std::views::transform(attack);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
using reversed_attacked_iota_t =
|
||
|
decltype(std::views::iota(static_cast<elem_t>(0), static_cast<elem_t>(0)) |
|
||
|
std::views::transform(reverse) | std::views::transform(attack));
|
||
|
|
||
|
struct progressive_descending_attack
|
||
|
: public strategy<reversed_attacked_iota_t> {
|
||
|
std::string name() const override {
|
||
|
return "progressive sequence that self-heals in the end";
|
||
|
}
|
||
|
reversed_attacked_iota_t elements() override {
|
||
|
return std::views::iota(static_cast<elem_t>(1), N_ELEMENTS + 1) |
|
||
|
std::views::transform(reverse) | std::views::transform(attack);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static elem_t shift(elem_t x) { return x << 32; }
|
||
|
using shifted_iota_t =
|
||
|
decltype(std::views::iota(static_cast<elem_t>(0), static_cast<elem_t>(0)) |
|
||
|
std::views::transform(shift));
|
||
|
|
||
|
struct hard_attack : public strategy<shifted_iota_t> {
|
||
|
std::string name() const override { return "carefully chosen numbers"; }
|
||
|
shifted_iota_t elements() override {
|
||
|
return std::views::iota(static_cast<elem_t>(0), N_ELEMENTS) |
|
||
|
std::views::transform(shift);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename C> void run_all(const std::string ¬e) {
|
||
|
std::cout << "\n«" << note << "»\n";
|
||
|
|
||
|
ascending_ordered_sequence{}.run(C{});
|
||
|
descending_ordered_sequence{}.run(C{});
|
||
|
progressive_ascending_attack{}.run(C{});
|
||
|
progressive_descending_attack{}.run(C{});
|
||
|
hard_attack{}.run(C{});
|
||
|
}
|
||
|
|
||
|
int main() {
|
||
|
run_all<std::unordered_set<elem_t>>("hash table");
|
||
|
run_all<std::set<elem_t>>("red-black tree");
|
||
|
|
||
|
return 0;
|
||
|
}
|