问题 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;

我已经在循环中运行了数千次,并且在我特定的3.40GHz桌面上我一直得到40 ns +/- 2 ns。

但是,当我想看看我能睡到的最短时间时:

#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 ns)=>睡眠58000 ns
  • sleep_for(3000 ns)=>睡眠57000 ns
  • sleep_for(2000 ns)=>睡眠时间为56000 ns
  • sleep_for(1000 ns)=>睡眠55000 ns
  • sleep_for(0 ns)=>睡眠54000 ns
  • sleep_for(-1000 ns)=>睡眠时间为313 ns
  • sleep_for(-2000 ns)=>睡眠时间为203 ns
  • sleep_for(-3000 ns)=>睡眠时间为215 ns
  • sleep_for(-4000 ns)=>睡眠时间为221 ns

我有一些问题:

  • 有什么可以解释这些数字?
  • 为什么在负时间内睡眠会返回200+ ns,而睡眠时间为0+纳秒会导致50,000+纳秒?
  • 负数作为睡眠时间是一个记录/支持的功能,还是我不小心偶然发现了一些我不能依赖的奇怪错误?
  • 是否有更好的C ++睡眠调用可以让我更加一致/可预测的睡眠时间?

8945
2017-08-06 04:09


起源

那才多久 nanosleep({0,1}, NULL) 需要(如果你有linux) - Cubbi
我无法复制这些结果,sleep_for(1ns)给我0ns。 - Rapptz
sleep_for 睡觉 至少 指定的持续时间。如果您提供负值,则根本不需要休眠。 sleep 但是,它是一种利基工具。你可能应该使用某种计时器机制(悲伤的标准C ++没有任何) - R. Martinho Fernandes
请注意你的消极 sleep_for 衡量的开销 sleep_for 调用:你不能指望它花零时间,因为它必须检查它是否应该采取零时间,这需要超过零时间!但是,请记住,如果你睡了一段时间,那么它就会睡7年零7个月。它总是符合睡眠时间超过要求的时间。如果你需要可笑的小睡眠时间,你需要忙碌循环,因为节省cpu的睡眠时间最终等待中断,而其他代码符文... - Yakk - Adam Nevraumont
在Linux内核中,调用nanosleep()的用户空间进程将在下一个调度程序唤醒间隔触发,通常为“jiffy”,除非该进程被标记为实时优先级。看到 elixir.free-electrons.com/linux/latest/source/kernel/time/... - moof2k


答案:


有什么可以解释这些数字?

有一个非常明显的模式,你的所有结果都比你要求睡觉的时间长54000ns。如果你看看GCC是怎么回事 this_thread::sleep_for() 在GNU / Linux上实现,你会看到它只是使用它 nanospleep 正如Cubbi的评论所说,称这个功能可能需要大约50000ns。我猜这个成本的一部分是进行系统调用,所以从用户空间切换到内核并返回。

为什么在负时间内睡眠会返回200+ ns,而睡眠时间为0+纳秒会导致50,000+纳秒?

猜测我会说C库会检查负数并且不会进行系统调用。

负数作为睡眠时间是一个记录/支持的功能,还是我不小心偶然发现了一些我不能依赖的奇怪错误?

标准不禁止传递否定参数,因此允许,并且函数应该“立即”返回,因为相对超时指定的时间已经过去。您不能依赖负参数返回比非负参数更快,这是您具体实现的假象。

是否有更好的C ++睡眠调用可以让我更加一致/可预测的睡眠时间?

我不这么认为 - 如果我知道一个,那么我们将在GCC中使用它来实现 this_thread::sleep_for()

编辑: 在更新版本的GCC的libstdc ++中,我添加了:

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

因此,当请求零或负持续时间时,将不会有系统调用。


12
2017-08-06 16:59



libstdc ++使用nanosleep实现this_thread :: sleep_for,如果tv_sec为负,nanosleep返回EINVAL(可能是立即)。 - Bulletmagnet