Skip to content

Commit

Permalink
test: Add perf test to measure the "cost" of chain wakeup
Browse files Browse the repository at this point in the history
The test creates a chain of future-promise-s and then wakes up the last
one thus causing the cascade of resolutions. There are 2x2 tests --
resolve with value or exception vs co_await-ed or .then()-d chains.

The result is (depth of 32)

test                   iterations      median         mad         min         max      allocs       tasks        inst      cycles
chain.then_value         29944640    33.158ns     0.026ns    33.059ns    33.740ns       1.063       1.094       314.9         0.0
chain.await_value        18963648    52.668ns     0.242ns    52.426ns    54.334ns       1.094       1.125       414.3         0.0
chain.then_exception     29095808    33.633ns     0.093ns    33.541ns    35.308ns       1.094       1.094       316.3         0.0
chain.await_exception      325344     3.112us    17.301ns     3.083us     3.192us       2.156       1.125     23489.0         0.0

Waking up co-await-ed chain with exceptions is extremely expensive.

The result with depth of 8 are the same, which means that exception
propagation via co-awaits is expensive on _every_ co_await, as it
"scales" linearly with the chain depth.

Signed-off-by: Pavel Emelyanov <[email protected]>
  • Loading branch information
xemul authored and avikivity committed Jan 20, 2025
1 parent e9c9cc3 commit 2312b7a
Showing 1 changed file with 67 additions and 0 deletions.
67 changes: 67 additions & 0 deletions tests/perf/future_util_perf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,73 @@ future<> immediate(int v, int& vs)
return make_ready_future<>();
}

struct chain {
static constexpr int scale = 32;
int value = 0;

[[gnu::noinline]]
future<> do_then(future<> w, int depth = scale) {
if (depth > 0) {
return do_then(std::move(w), depth - 1).then([this] {
value += 1;
perf_tests::do_not_optimize(value);
return make_ready_future<>();
});
} else {
return w;
}
}

[[gnu::noinline]]
future<> do_await(future<> w, int depth = scale) {
if (depth > 0) {
co_await do_await(std::move(w), depth - 1);
value += 1;
perf_tests::do_not_optimize(value);
} else {
co_await std::move(w);
}
}
};

PERF_TEST_F(chain, then_value)
{
promise<> p;
auto f = do_then(p.get_future());
p.set_value();
return f.then([] { return scale; });
}

PERF_TEST_F(chain, await_value)
{
promise<> p;
auto f = do_await(p.get_future());
p.set_value();
return f.then([] { return scale; });
}

PERF_TEST_F(chain, then_exception)
{
promise<> p;
auto f = do_then(p.get_future());
p.set_exception(std::runtime_error(""));
return f.then_wrapped([] (auto x) {
x.ignore_ready_future();
return scale;
});
}

PERF_TEST_F(chain, await_exception)
{
promise<> p;
auto f = do_await(p.get_future());
p.set_exception(std::runtime_error(""));
return f.then_wrapped([] (auto x) {
x.ignore_ready_future();
return scale;
});
}

PERF_TEST_F(parallel_for_each, immediate_1)
{
auto&& begin = range.begin();
Expand Down

0 comments on commit 2312b7a

Please sign in to comment.