Make pipeline policy/topology configurable
This commit is contained in:
@@ -24,15 +24,15 @@ int main() {
|
||||
}
|
||||
});
|
||||
|
||||
StaticThreadPipeline<std::latch *, WaitStrategy::WaitIfStageEmpty, 1>
|
||||
pipeline(LOG_PIPELINE_SIZE);
|
||||
ThreadPipeline<std::latch *> pipeline(WaitStrategy::WaitIfStageEmpty, {1},
|
||||
LOG_PIPELINE_SIZE);
|
||||
|
||||
std::latch done{0};
|
||||
|
||||
// Stage 0 consumer thread
|
||||
std::thread stage0_thread([&pipeline, &done]() {
|
||||
for (;;) {
|
||||
auto guard = pipeline.acquire<0, 0>();
|
||||
auto guard = pipeline.acquire(0, 0);
|
||||
|
||||
for (auto &item : guard.batch) {
|
||||
spend_cpu_cycles(BUSY_ITERS);
|
||||
@@ -89,15 +89,15 @@ int main() {
|
||||
.warmup(100);
|
||||
|
||||
for (int batch_size : {1, 4, 16, 64, 256}) {
|
||||
StaticThreadPipeline<std::latch *, WaitStrategy::WaitIfStageEmpty, 1>
|
||||
pipeline(LOG_PIPELINE_SIZE);
|
||||
ThreadPipeline<std::latch *> pipeline(WaitStrategy::WaitIfStageEmpty, {1},
|
||||
LOG_PIPELINE_SIZE);
|
||||
|
||||
std::latch done{0};
|
||||
|
||||
// Stage 0 consumer thread
|
||||
std::thread stage0_thread([&pipeline, &done]() {
|
||||
for (;;) {
|
||||
auto guard = pipeline.acquire<0, 0>();
|
||||
auto guard = pipeline.acquire(0, 0);
|
||||
|
||||
for (auto &item : guard.batch) {
|
||||
spend_cpu_cycles(BUSY_ITERS);
|
||||
@@ -142,74 +142,73 @@ int main() {
|
||||
}
|
||||
|
||||
// Helper function for wait strategy benchmarks
|
||||
auto benchmark_wait_strategy =
|
||||
[]<WaitStrategy strategy>(const std::string &name,
|
||||
ankerl::nanobench::Bench &bench) {
|
||||
constexpr int LOG_PIPELINE_SIZE =
|
||||
8; // Smaller buffer to increase contention
|
||||
constexpr int NUM_ITEMS = 50'000;
|
||||
constexpr int BATCH_SIZE = 4; // Small batches to increase coordination
|
||||
constexpr int BUSY_ITERS =
|
||||
10; // Light work to emphasize coordination overhead
|
||||
auto benchmark_wait_strategy = [](WaitStrategy strategy,
|
||||
const std::string &name,
|
||||
ankerl::nanobench::Bench &bench) {
|
||||
constexpr int LOG_PIPELINE_SIZE =
|
||||
8; // Smaller buffer to increase contention
|
||||
constexpr int NUM_ITEMS = 50'000;
|
||||
constexpr int BATCH_SIZE = 4; // Small batches to increase coordination
|
||||
constexpr int BUSY_ITERS =
|
||||
10; // Light work to emphasize coordination overhead
|
||||
|
||||
StaticThreadPipeline<std::latch *, strategy, 1, 1> pipeline(
|
||||
LOG_PIPELINE_SIZE);
|
||||
ThreadPipeline<std::latch *> pipeline(strategy, {1, 1}, LOG_PIPELINE_SIZE);
|
||||
|
||||
std::latch done{0};
|
||||
std::latch done{0};
|
||||
|
||||
// Stage 0 worker
|
||||
std::thread stage0_thread([&pipeline, &done]() {
|
||||
for (;;) {
|
||||
auto guard = pipeline.template acquire<0, 0>();
|
||||
for (auto &item : guard.batch) {
|
||||
spend_cpu_cycles(BUSY_ITERS);
|
||||
if (item == &done)
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Stage 1 worker (final stage - always calls futex wake)
|
||||
std::thread stage1_thread([&pipeline, &done]() {
|
||||
for (;;) {
|
||||
auto guard = pipeline.template acquire<1, 0>();
|
||||
for (auto &item : guard.batch) {
|
||||
spend_cpu_cycles(BUSY_ITERS);
|
||||
if (item == &done)
|
||||
return;
|
||||
if (item)
|
||||
item->count_down();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bench.run(name, [&] {
|
||||
int items_pushed = 0;
|
||||
while (items_pushed < NUM_ITEMS - 1) {
|
||||
auto guard = pipeline.push(
|
||||
std::min(NUM_ITEMS - 1 - items_pushed, BATCH_SIZE), true);
|
||||
auto it = guard.batch.begin();
|
||||
items_pushed += guard.batch.size();
|
||||
for (size_t i = 0; i < guard.batch.size(); ++i, ++it) {
|
||||
*it = nullptr;
|
||||
}
|
||||
}
|
||||
std::latch finish{1};
|
||||
{
|
||||
auto guard = pipeline.push(1, true);
|
||||
guard.batch[0] = &finish;
|
||||
}
|
||||
finish.wait();
|
||||
});
|
||||
|
||||
// Shutdown
|
||||
{
|
||||
auto guard = pipeline.push(1, true);
|
||||
guard.batch[0] = &done;
|
||||
// Stage 0 worker
|
||||
std::thread stage0_thread([&pipeline, &done]() {
|
||||
for (;;) {
|
||||
auto guard = pipeline.acquire(0, 0);
|
||||
for (auto &item : guard.batch) {
|
||||
spend_cpu_cycles(BUSY_ITERS);
|
||||
if (item == &done)
|
||||
return;
|
||||
}
|
||||
stage0_thread.join();
|
||||
stage1_thread.join();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Stage 1 worker (final stage - always calls futex wake)
|
||||
std::thread stage1_thread([&pipeline, &done]() {
|
||||
for (;;) {
|
||||
auto guard = pipeline.acquire(1, 0);
|
||||
for (auto &item : guard.batch) {
|
||||
spend_cpu_cycles(BUSY_ITERS);
|
||||
if (item == &done)
|
||||
return;
|
||||
if (item)
|
||||
item->count_down();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bench.run(name, [&] {
|
||||
int items_pushed = 0;
|
||||
while (items_pushed < NUM_ITEMS - 1) {
|
||||
auto guard = pipeline.push(
|
||||
std::min(NUM_ITEMS - 1 - items_pushed, BATCH_SIZE), true);
|
||||
auto it = guard.batch.begin();
|
||||
items_pushed += guard.batch.size();
|
||||
for (size_t i = 0; i < guard.batch.size(); ++i, ++it) {
|
||||
*it = nullptr;
|
||||
}
|
||||
}
|
||||
std::latch finish{1};
|
||||
{
|
||||
auto guard = pipeline.push(1, true);
|
||||
guard.batch[0] = &finish;
|
||||
}
|
||||
finish.wait();
|
||||
});
|
||||
|
||||
// Shutdown
|
||||
{
|
||||
auto guard = pipeline.push(1, true);
|
||||
guard.batch[0] = &done;
|
||||
}
|
||||
stage0_thread.join();
|
||||
stage1_thread.join();
|
||||
};
|
||||
|
||||
// Wait strategy comparison benchmark - multiple stages to trigger futex wakes
|
||||
{
|
||||
@@ -220,12 +219,11 @@ int main() {
|
||||
.relative(true)
|
||||
.warmup(50);
|
||||
|
||||
benchmark_wait_strategy.template operator()<WaitStrategy::WaitIfStageEmpty>(
|
||||
"WaitIfStageEmpty", bench);
|
||||
benchmark_wait_strategy.template
|
||||
operator()<WaitStrategy::WaitIfUpstreamIdle>("WaitIfUpstreamIdle", bench);
|
||||
benchmark_wait_strategy.template operator()<WaitStrategy::Never>("Never",
|
||||
bench);
|
||||
benchmark_wait_strategy(WaitStrategy::WaitIfStageEmpty, "WaitIfStageEmpty",
|
||||
bench);
|
||||
benchmark_wait_strategy(WaitStrategy::WaitIfUpstreamIdle,
|
||||
"WaitIfUpstreamIdle", bench);
|
||||
benchmark_wait_strategy(WaitStrategy::Never, "Never", bench);
|
||||
}
|
||||
|
||||
// TODO: Add more benchmarks for:
|
||||
|
||||
Reference in New Issue
Block a user