Actually have contention in benchmark

This commit is contained in:
2025-08-29 17:05:21 -04:00
parent 91e799aae8
commit 5592d065de
2 changed files with 100 additions and 155 deletions

View File

@@ -20,29 +20,14 @@ struct ContentionEnvironment {
std::unique_ptr<std::latch> contention_latch;
std::unique_ptr<std::latch> render_latch;
// Metrics for testing
metric::Family<metric::Counter> counter_family;
metric::Family<metric::Gauge> gauge_family;
metric::Family<metric::Histogram> histogram_family;
metric::Family<metric::Gauge> gauge_family =
metric::create_gauge("gauge", "");
metric::Family<metric::Counter> counter_family =
metric::create_counter("counter", "");
metric::Family<metric::Histogram> histogram_family = metric::create_histogram(
"histogram", "", metric::exponential_buckets(0.001, 5, 7));
// Test instances
metric::Counter counter;
metric::Gauge gauge;
metric::Histogram histogram;
ContentionEnvironment()
: counter_family(
metric::create_counter("bench_counter", "Benchmark counter")),
gauge_family(metric::create_gauge("bench_gauge", "Benchmark gauge")),
histogram_family(
metric::create_histogram("bench_histogram", "Benchmark histogram",
// 7 explicit buckets + automatic +Inf = 8
// total (optimal for SIMD: 2x4 buckets)
std::initializer_list<double>{
0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 25.0})),
counter(counter_family.create({{"benchmark", "contention"}})),
gauge(gauge_family.create({{"benchmark", "contention"}})),
histogram(histogram_family.create({{"benchmark", "contention"}})) {}
ContentionEnvironment() = default;
void start_background_contention(int num_threads = 4) {
stop_flag.store(false);
@@ -50,12 +35,9 @@ struct ContentionEnvironment {
for (int i = 0; i < num_threads; ++i) {
background_threads.emplace_back([this, i]() {
// Each background thread creates its own metrics to avoid conflicts
auto bg_counter =
counter_family.create({{"thread", std::to_string(i)}});
auto bg_gauge = gauge_family.create({{"bg_thread", std::to_string(i)}});
auto bg_histogram =
histogram_family.create({{"bg_thread", std::to_string(i)}});
auto bg_counter = counter_family.create({});
auto bg_gauge = gauge_family.create({});
auto bg_histogram = histogram_family.create({});
std::mt19937 rng(i);
std::uniform_real_distribution<double> dist(0.0, 10.0);
@@ -67,6 +49,7 @@ struct ContentionEnvironment {
// Simulate mixed workload
bg_counter.inc(1.0);
bg_gauge.set(dist(rng));
bg_gauge.inc(1.0);
bg_histogram.observe(dist(rng));
}
});
@@ -111,21 +94,24 @@ int main() {
ankerl::nanobench::Bench bench;
bench.title("WeaselDB Metrics Performance").unit("operation").warmup(1000);
metric::Family<metric::Gauge> gauge_family =
metric::create_gauge("gauge", "");
metric::Family<metric::Counter> counter_family =
metric::create_counter("counter", "");
metric::Family<metric::Histogram> histogram_family = metric::create_histogram(
"histogram", "", metric::exponential_buckets(0.001, 5, 7));
auto counter = counter_family.create({});
auto gauge = gauge_family.create({});
auto histogram = histogram_family.create({});
// Baseline performance without contention
{
auto counter_family =
metric::create_counter("baseline_counter", "Baseline counter");
auto counter = counter_family.create({{"type", "baseline"}});
bench.run("counter.inc() - no contention", [&]() {
counter.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(counter);
});
auto gauge_family =
metric::create_gauge("baseline_gauge", "Baseline gauge");
auto gauge = gauge_family.create({{"type", "baseline"}});
bench.run("gauge.inc() - no contention", [&]() {
gauge.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(gauge);
@@ -136,11 +122,6 @@ int main() {
ankerl::nanobench::doNotOptimizeAway(gauge);
});
auto histogram_family = metric::create_histogram(
"baseline_histogram", "Baseline histogram",
std::initializer_list<double>{0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 25.0});
auto histogram = histogram_family.create({{"type", "baseline"}});
bench.run("histogram.observe() - no contention", [&]() {
histogram.observe(0.5);
ankerl::nanobench::doNotOptimizeAway(histogram);
@@ -154,25 +135,15 @@ int main() {
// Start background threads creating contention
env.start_background_contention(8);
bench.run("counter.inc() - 8 background threads", [&]() {
env.counter.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(env.counter);
});
bench.run("counter.inc() - 8 background threads",
[&]() { counter.inc(1.0); });
bench.run("gauge.inc() - 8 background threads", [&]() {
env.gauge.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(env.gauge);
});
bench.run("gauge.inc() - 8 background threads", [&]() { gauge.inc(1.0); });
bench.run("gauge.set() - 8 background threads", [&]() {
env.gauge.set(42.0);
ankerl::nanobench::doNotOptimizeAway(env.gauge);
});
bench.run("gauge.set() - 8 background threads", [&]() { gauge.set(42.0); });
bench.run("histogram.observe() - 8 background threads", [&]() {
env.histogram.observe(1.5);
ankerl::nanobench::doNotOptimizeAway(env.histogram);
});
bench.run("histogram.observe() - 8 background threads",
[&]() { histogram.observe(1.5); });
}
// Concurrent render contention
@@ -183,58 +154,14 @@ int main() {
env.start_background_contention(4);
env.start_render_thread();
bench.run("counter.inc() - with concurrent render", [&]() {
env.counter.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(env.counter);
});
bench.run("counter.inc() - with concurrent render",
[&]() { counter.inc(1.0); });
bench.run("gauge.inc() - with concurrent render", [&]() {
env.gauge.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(env.gauge);
});
bench.run("gauge.inc() - with concurrent render",
[&]() { gauge.inc(1.0); });
bench.run("histogram.observe() - with concurrent render", [&]() {
env.histogram.observe(2.0);
ankerl::nanobench::doNotOptimizeAway(env.histogram);
});
}
// Shared gauge contention
{
// Test the multi-writer CAS behavior of gauges when multiple threads
// create gauges with the same labels. They will all point to the same
// underlying state, causing high contention.
auto gauge_family =
metric::create_gauge("shared_gauge", "Shared gauge test");
std::atomic<bool> stop_shared{false};
std::vector<std::thread> shared_threads;
std::latch start_latch{
9}; // Force threads to start concurrently (8 background + 1 benchmark)
for (int i = 0; i < 8; ++i) {
shared_threads.emplace_back(
[&gauge_family, &stop_shared, &start_latch]() {
auto gauge = gauge_family.create({{"shared", "true"}});
start_latch.arrive_and_wait(); // All threads start together
while (!stop_shared.load(std::memory_order_relaxed)) {
gauge.inc(1.0);
}
});
}
auto gauge_for_benchmark = gauge_family.create({{"shared", "true"}});
start_latch
.arrive_and_wait(); // Benchmark thread waits for all background threads
bench.run("gauge.inc() - 8 threads, same labels (contention)", [&]() {
gauge_for_benchmark.inc(1.0);
ankerl::nanobench::doNotOptimizeAway(gauge_for_benchmark);
});
stop_shared.store(true);
for (auto &t : shared_threads) {
t.join();
}
bench.run("histogram.observe() - with concurrent render",
[&]() { histogram.observe(2.0); });
}
// Render performance scaling