blob: b58e824bb8f7d5f7b42615b7b6f7e36b99c4a0d9 [file] [log] [blame]
// Copyright (C) 2020-2026 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-O3" }
// { dg-do run { target c++11 } }
// { dg-additional-options "-pthread" { target pthread } }
// { dg-require-gthreads "" }
// { dg-skip-if "no high resolution timer support" { hppa*-*-linux* } }
#include <future>
#include <chrono>
#include <iostream>
#include <testsuite_hooks.h>
int iterations = 200;
using namespace std;
template<typename Duration>
double
print(const char* desc, Duration dur)
{
auto ns = chrono::duration_cast<chrono::nanoseconds>(dur).count();
double d = double(ns) / iterations;
cout << desc << ": " << ns << "ns for " << iterations
<< " calls, avg " << d << "ns per call\n";
return d;
}
static void
calibrate()
{
/* After set_value, wait_for is faster, so use that for the
calibration loops to avoid zero at low clock resultions. */
promise<int> p = {};
future<int> f = p.get_future();
p.set_value(1);
auto start = chrono::high_resolution_clock::now();
auto stop = start;
/* Loop until the clock advances, so that start is right after a
time increment. */
do
stop = chrono::high_resolution_clock::now();
while (start == stop);
/* This approximates the smallest time increment we may expect to be
able to measure. It doesn't have to be very precise, just a
ballpart of the right magnitude. */
auto tick = stop - start;
int i = 0;
start = stop;
/* Now until the clock advances again, so that stop is right
after another time increment. */
do
{
f.wait_for(chrono::seconds(0));
stop = chrono::high_resolution_clock::now();
i++;
}
while (start == stop);
/* Aim for some 10 ticks. This won't be quite right if now() takes
up a significant portion of the loop time, but we'll measure
without that and adjust in the loop below. */
if (iterations < i * 10)
iterations = i * 10;
/* We aim for some 10 ticks for the loop that's expected to be fastest,
but even if we don't get quite that many, we're still fine. */
iterations /= 2;
do
{
iterations *= 2;
start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_for(chrono::seconds(0));
stop = chrono::high_resolution_clock::now();
}
while (stop - start < 5 * tick);
}
int main()
{
/* First, calibrate the iteration count so that we don't get any of
the actual measurement loops to complete in less than the clock
granularity. */
calibrate ();
promise<int> p;
future<int> f = p.get_future();
auto start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_for(chrono::seconds(0));
auto stop = chrono::high_resolution_clock::now();
double wait_for_0 = print("wait_for(0s)", stop - start);
start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_until(chrono::system_clock::time_point::min());
stop = chrono::high_resolution_clock::now();
double wait_until_sys_min __attribute__((unused))
= print("wait_until(system_clock minimum)", stop - start);
start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_until(chrono::steady_clock::time_point::min());
stop = chrono::high_resolution_clock::now();
double wait_until_steady_min __attribute__((unused))
= print("wait_until(steady_clock minimum)", stop - start);
start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_until(chrono::system_clock::time_point());
stop = chrono::high_resolution_clock::now();
double wait_until_sys_epoch __attribute__((unused))
= print("wait_until(system_clock epoch)", stop - start);
start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_until(chrono::steady_clock::time_point());
stop = chrono::high_resolution_clock::now();
double wait_until_steady_epoch __attribute__((unused))
= print("wait_until(steady_clock epoch", stop - start);
p.set_value(1);
start = chrono::high_resolution_clock::now();
for(int i = 0; i < iterations; i++)
f.wait_for(chrono::seconds(0));
stop = chrono::high_resolution_clock::now();
double ready = print("wait_for when ready", stop - start);
// Polling before ready with wait_for(0s) should be almost as fast as
// after the result is ready.
VERIFY( wait_for_0 < (ready * 30) );
// Polling before ready using wait_until(min) should not be terribly
// slow. We hope for no more than 100x slower, but a little over
// 100x has been observed, and since the measurements may have a lot
// of noise, and increasing the measurement precision through
// additional iterations would make the test run for too long on
// systems with very low clock precision (60Hz clocks are not
// unheard of), we tolerate a lot of error.
VERIFY( wait_until_sys_min < (ready * 200) );
VERIFY( wait_until_steady_min < (ready * 200) );
// The following two tests fail with GCC 11, see
// https://gcc.gnu.org/pipermail/libstdc++/2020-November/051422.html
#if 0
// Polling before ready using wait_until(epoch) should not be terribly slow.
VERIFY( wait_until_sys_epoch < (ready * 200) );
VERIFY( wait_until_steady_epoch < (ready * 200) );
#endif
}