Std:: this_thread:: sleep_for() и наносекунды

Если я поставил два вызова бок о бок, чтобы определить наименьшую измеряемую продолжительность времени:

// g++ -std=c++11 -O3 -Wall test.cpp
#include <chrono>
typedef std::chrono::high_resolution_clock hrc;

hrc::time_point start = hrc::now();
hrc::time_point end   = hrc::now();
std::chrono::nanoseconds duration = end - start;
std::cout << "duration: " << duration.count() << " ns" << std::endl;

Я запускаю это тысячи раз в цикле, и я постоянно получаю 40 нс +/- 2 нс на моем рабочем столе 3.40 ГГц.

Однако, когда я смотрю, чтобы узнать, что является самым коротким временем, я могу спать:

#include <thread>

hrc::time_point start = hrc::now();
std::this_thread::sleep_for( std::chrono::nanoseconds(1) );
hrc::time_point end   = hrc::now();
std::chrono::nanoseconds duration = end - start;
std::cout << "slept for: " << duration.count() << " ns" << std::endl;

Это говорит мне, что я спал в среднем 55400 наносекунд или 55,4 микросекунды. Гораздо больше, чем ожидалось.

Поместите вышеуказанный код в цикл for(), я попробовал спать для разных сумм, и это результат:

  • sleep_for (4000 нс) = > спал за 58000 нс
  • sleep_for (3000 нс) = > спал за 57000 нс
  • sleep_for (2000 нс) = > спал за 56000 нс
  • sleep_for (1000 нс) = > спал за 55000 нс
  • sleep_for (0 ns) = > спал за 54000 нс
  • sleep_for (-1000 ns) = > спал за 313 нс
  • sleep_for (-2000 нс) = > спал за 203 нс
  • sleep_for (-3000 нс) = > спал за 215 нс
  • sleep_for (-4000 нс) = > спал за 221 нс

Некоторые вопросы, которые у меня есть:

  • Что могли бы объяснить эти цифры?
  • Почему спать для отрицательного количества времени возвращается 200+ нс, а спать за 0+ наносекунд приводит к 50 000 + наносекундам?
  • Являются ли отрицательные числа как время сна документированной/поддерживаемой функцией или случайно случайно наткнулись на какую-то странную ошибку, на которую я не могу положиться?
  • Есть ли лучший вызов сна С++, который даст мне более последовательное/предсказуемое время сна?

Ответ 1

Что могли бы объяснить эти цифры?

Там довольно очевидный шаблон, все ваши результаты постоянно на 54000ns больше, чем время, когда вы просите спать. Если вы посмотрите, как GCC this_thread::sleep_for() реализован на GNU/Linux, вы увидите, что он просто использует nanospleep, и, как говорит Cubbi, вызов этой функции может занять около 50000ns. Я бы предположил, что некоторые из этих затрат составляют системный вызов, поэтому переход из пользовательского пространства в ядро ​​и обратно.

Почему спать для отрицательного количества времени возвращается 200+ нс, а спать за 0+ наносекунд приводит к 50 000 + наносекундам?

В предположении я бы сказал, что библиотека C проверяет отрицательное число и не делает системный вызов.

Является ли отрицательное число как время сна документированной/поддерживаемой функцией, или я случайно наткнулся на какую-то странную ошибку, на которую я не могу положиться?

Стандарт не запрещает передавать отрицательные аргументы, поэтому он разрешен, и функция должна возвращаться "немедленно", потому что время, указанное относительным таймаутом, уже прошло. Вы не можете полагаться на отрицательные аргументы, возвращающие быстрее неотрицательных аргументов, хотя это артефакт вашей конкретной реализации.

Есть ли лучший вызов сна С++, который даст мне более последовательное/предсказуемое время сна?

Я так не думаю - если бы я знал об этом, мы бы использовали его в GCC для реализации this_thread::sleep_for().

Изменить: В более поздних версиях GCC libstdС++ я добавил:

if (__rtime <= __rtime.zero())
  return;

поэтому не будет никакого системного вызова при запросе нулевой или отрицательной длительности.

Ответ 2

в ядре init/init_task.c в структуре task_struct init_task определен параметр

.timer_slack_ns = 50000, /* 50 usec default slack */

который добавлен к не RT процессам в функции ядра hrtimer_nanosleep(), чтобы сделать таймеры hardirqs реже.