Since for a C++20 coroutine the compiler has to create a different code than for an ordinary function by putting all local variables in heap instead of stack, what is expected slowdown of coroutine function relative to an ordinary function making the same computation?
I wrote a simple test that measures the time of summation of a large number of values:
#include <iostream>
#include <chrono>
#include <coroutine>
constexpr size_t N = 1024ull*1024ull*1024ull;
double compute()
{
double res = 0;
for ( size_t i = 0; i < N; ++i )
res += i;
return res;
}
template<typename F>
auto timer( F f )
{
using namespace std::chrono;
auto s = high_resolution_clock::now();
auto res = f();
nanoseconds dur = high_resolution_clock::now() - s;
std::cout << "duration: " << dur.count() * 1e-9 << " sec\n";
return res;
}
struct Future {
struct promise_type {
double value_;
Future get_return_object() { return { std::coroutine_handle<promise_type>::from_promise(*this) }; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
void return_value(double v) { value_ = v; }
};
bool done() { return h_.done(); }
void resume() { return h_(); }
double value() { return h_.promise().value_; }
~Future() { h_.destroy(); }
std::coroutine_handle<promise_type> h_;
};
double computeInCoroutine()
{
auto future = []() -> Future
{
double res = 0;
for ( size_t i = 0; i < N; ++i )
res += i;
co_return res;
}();
while (!future.done())
future.resume();
return future.value();
}
int main()
{
std::cout << "Ordinary function ";
auto res = timer( compute );
std::cout << "Coroutine ";
res += timer( computeInCoroutine );
return (int)res;
}
In Visual Studio 2019 16.10.3 I get the result:
Ordinary function duration: 0.793619 sec
Coroutine duration: 1.05897 sec
Please note that the times were obtained on a local computer and they are very repeatable. I would not recommend measure the time online (e.g. in godbold.org), since it is very unstable there.
So just by converting an ordinary function in a coroutine that does not suspend in the middle of computation, in MSVC we get about 30% of performance penalty, or the comparison is not fair?
Update. After I increased the number of iterations in 16 times:
constexpr size_t N = 16ull*1024ull*1024ull*1024ull;
the difference in performance of two functions became really dramatic:
Ordinary function duration: 12.602 sec
Coroutine duration: 45.5615 sec