mirror of
https://github.com/mfocko/blog.git
synced 2025-05-08 04:12:59 +02:00
deploy: dadb0d51f7
This commit is contained in:
parent
dbd784b359
commit
0b9bf3d392
407 changed files with 1120 additions and 338 deletions
files
algorithms
graphs
hash-tables/breaking
recursion
time-complexity
c/bonuses
Binary file not shown.
Binary file not shown.
133
files/algorithms/hash-tables/breaking/benchmark.cpp
Normal file
133
files/algorithms/hash-tables/breaking/benchmark.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
#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;
|
||||
}
|
118
files/algorithms/hash-tables/breaking/benchmark.py
Normal file
118
files/algorithms/hash-tables/breaking/benchmark.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from functools import cached_property
|
||||
from time import monotonic_ns
|
||||
|
||||
N_ELEMENTS = 10_000_000
|
||||
LOOPS = 10
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self, data_structure=set):
|
||||
self._table = data_structure()
|
||||
|
||||
@cached_property
|
||||
def elements(self):
|
||||
raise NotImplementedError("Implement for each strategy")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
raise NotImplementedError("Implement for each strategy")
|
||||
|
||||
def run(self):
|
||||
print(f"\nBenchmarking:\t\t{self.name}")
|
||||
|
||||
# Extract the elements here, so that the evaluation of them does not
|
||||
# slow down the relevant part of benchmark
|
||||
elements = self.elements
|
||||
|
||||
# Insertion phase
|
||||
start = monotonic_ns()
|
||||
for x in elements:
|
||||
self._table.add(x)
|
||||
after_insertion = monotonic_ns()
|
||||
|
||||
print(f"Insertion phase:\t{(after_insertion - start) / 1000000:.2f}ms")
|
||||
|
||||
# Lookup phase
|
||||
start = monotonic_ns()
|
||||
for _ in range(LOOPS):
|
||||
for x in elements:
|
||||
assert x in self._table
|
||||
after_lookups = monotonic_ns()
|
||||
|
||||
print(f"Lookup phase:\t\t{(after_lookups - start) / 1000000:.2f}ms")
|
||||
|
||||
|
||||
class AscendingOrderedSequence(Strategy):
|
||||
@property
|
||||
def name(self):
|
||||
return "ordered sequence (ascending)"
|
||||
|
||||
@cached_property
|
||||
def elements(self):
|
||||
return [x for x in range(N_ELEMENTS)]
|
||||
|
||||
|
||||
class DescendingOrderedSequence(Strategy):
|
||||
@property
|
||||
def name(self):
|
||||
return "ordered sequence (descending)"
|
||||
|
||||
@cached_property
|
||||
def elements(self):
|
||||
return [x for x in reversed(range(N_ELEMENTS))]
|
||||
|
||||
|
||||
class ProgressiveAttack(Strategy):
|
||||
@staticmethod
|
||||
def _break(n):
|
||||
return n << max(5, n.bit_length())
|
||||
|
||||
|
||||
class ProgressiveAscendingAttack(ProgressiveAttack):
|
||||
@property
|
||||
def name(self):
|
||||
return "progressive sequence that self-heals on resize"
|
||||
|
||||
@cached_property
|
||||
def elements(self):
|
||||
return [self._break(x) for x in range(N_ELEMENTS)]
|
||||
|
||||
|
||||
class ProgressiveDescendingAttack(ProgressiveAttack):
|
||||
@property
|
||||
def name(self):
|
||||
return "progressive sequence that self-heals in the end"
|
||||
|
||||
@cached_property
|
||||
def elements(self):
|
||||
return [self._break(x) for x in reversed(range(N_ELEMENTS))]
|
||||
|
||||
|
||||
class HardAttack(Strategy):
|
||||
@property
|
||||
def name(self):
|
||||
return "carefully chosen numbers"
|
||||
|
||||
@cached_property
|
||||
def elements(self):
|
||||
return [x << 32 for x in range(N_ELEMENTS)]
|
||||
|
||||
|
||||
STRATEGIES = [
|
||||
AscendingOrderedSequence,
|
||||
DescendingOrderedSequence,
|
||||
ProgressiveAscendingAttack,
|
||||
ProgressiveDescendingAttack,
|
||||
HardAttack,
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
for strategy in STRATEGIES:
|
||||
strategy().run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue