std :: system_clock和std :: steady_clock之间的区别?


97

std::system_clock和之间有什么区别std::steady_clock?(一个示例示例说明不同的结果/行为将是很好的)。

如果我的目标是精确测量函数的执行时间(如基准),那么std::system_clockstd::steady_clock和之间的最佳选择是std::high_resolution_clock什么?


9
首先,system_clock可能不稳定。
James McNellis

12
@CharlesSalvia我不能说其他平台,但是system_clock在Windows上并不稳定。在Windows上,任何具有足够特权的用户都可以将系统时间更改为任意值。此外,如果需要,时间同步服务可以向后调整系统时间。我希望大多数其他平台具有类似的功能,可以调整系统时间。
James McNellis 2015年

3
@查尔斯:我所知道的大多数POSIX盒都受到类似的影响,如果用户更改时间,它们的时间也会改变。
Billy ONeal 2015年

5
该问题的视频答案:youtube.com/watch?
v=P32hvk8b13M&t=48m44s

1
@CharlesSalvia。根据我自己分析数十个PC数据采集系统的定时输出的经验,来自计算机的时间不稳定。Linux,Windows和使用的特定系统调用是未知的,但是共同点是后续时间值之间经常出现负时间差。直线时间不是常态。
泰森·希尔默

Answers:


71

从N3376:

20.11.7.1 [time.clock.system] / 1:

类的对象system_clock表示系统范围实时时钟的挂钟时间。

20.11.7.2 [time.clock.steady] / 1:

类的对象steady_clock表示时钟,其值time_point永不随物理时间的增长而减少,并且其值time_point相对于实时以稳定的速率增长。即,时钟可能无法调整。

20.11.7.3 [time.clock.hires] / 1:

类的对象high_resolution_clock表示滴答周期最短的时钟。high_resolution_clock可能是system_clock或的同义词steady_clock

例如,系统范围的时钟可能会受到夏时制之类的影响,此时,将来某个时刻列出的实际时间实际上可能是过去的时间。(例如,在美国,下降时间向后移动了一个小时,因此同一小时经历了“两次”)。但是,steady_clock不允许受这种情况的影响。

在这种情况下,考虑“稳定”的另一种方法是在表20.11.3 [time.clock.req] / 2中定义的要求:

在表59中C1C2表示时钟类型。t1t2是返回值C1::now(),该值由调用返回t1发生在调用返回之前发生的位置t2而这两个调用发生在之前C1::time_point::max()。[注意:这意味着C1t1和之间没有回绕t2。—尾注]

表达式:C1::is_steady
返回:const bool
操作语义:true如果t1 <= t2始终为true,并且时钟滴答之间的时间为常数,否则为false

这就是所有标准之间的差异。

如果您想进行基准测试,最好的选择可能是std::high_resolution_clock,因为您的平台可能QueryPerformanceCounter为此时钟使用了高分辨率计时器(例如Windows)。但是,如果要进行基准测试,则应考虑使用特定于平台的计时器进行基准测试,因为不同的平台对此处理方式有所不同。例如,某些平台可能会为您提供确定所需程序的实际时钟滴答数的方法(独立于同一CPU上运行的其他进程)。更好的是,动手使用真正的探查器并使用它。


1
@查尔斯:小心指出标准在哪里?似乎清楚地表明了相反的情况。
Billy ONeal 2015年

9
@Charles:另外,POSIX时间也不是“稳定”的-如果用户更改了计算机上的时间设置,则POSIX时间将会改变。如果您正在煮鸡蛋,并且需要持续4分钟的计时器,那么即使当前时间已更改,您也需要持续4分钟。如果您在5号3点为会议设置了计时器,那么如果当地时间更改,则绝对需要更改该计时器。因此,之间的区别steady_clock,并system_clock在这里。
Billy ONeal 2015年

1
@ 5gon:不需要system_clockUTC。
比利·奥尼尔

1
@CharlesSalvia另请注意,由于POSIX时间与UTC相关,并且UTC具有leap秒(请参阅en.wikipedia.org/wiki/Unix_time#Leap_seconds)。这意味着,即使从不调整机器上的时间,C / POSIX时间也可能是非单调的。
Michael Schlottke-Lakemper '16

3
更新(Visual Studio 2015)steady_clock的实现已更改[.....] stable_clock现在基于QueryPerformanceCounter(),并且high_resolution_clock现在是stable_clock的typedef。引用自msdn.microsoft.com/en-us/library/hh874757.aspx
felix-b

47

Billy根据我完全同意的ISO C ++标准提供了一个很好的答案。然而,故事还有另一面-现实生活。现在看来,在流行的编译器实现中,这些时钟之间确实没有任何区别:

gcc 4.8:

#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
   ...
#else
  typedef system_clock steady_clock;
#endif
  typedef system_clock high_resolution_clock;

Visual Studio 2012:

class steady_clock : public system_clock
{   // wraps monotonic clock
public:
  static const bool is_monotonic = true;    // retained
  static const bool is_steady = true;
};

typedef system_clock high_resolution_clock;

在使用gcc的情况下,您可以通过检查is_steady并相应地执行操作来检查是否处理稳定的时钟。但是VS2012似乎在这里有点作弊:-)

如果您需要高精度时钟,我建议您现在编写符合C ++ 11官方时钟接口的自己的时钟,并等待实现赶上来。与直接在代码中使用特定于操作系统的API相比,这将是更好的方法。对于Windows,您可以这样:

// Self-made Windows QueryPerformanceCounter based C++11 API compatible clock
struct qpc_clock {
  typedef std::chrono::nanoseconds                       duration;      // nanoseconds resolution
  typedef duration::rep                                  rep;
  typedef duration::period                               period;
  typedef std::chrono::time_point<qpc_clock, duration>   time_point;
  static bool is_steady;                                                // = true
  static time_point now()
  {
    if(!is_inited) {
      init();
      is_inited = true;
    }
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    return time_point(duration(static_cast<rep>((double)counter.QuadPart / frequency.QuadPart *
                                                period::den / period::num)));
  }

private:
  static bool is_inited;                                                // = false
  static LARGE_INTEGER frequency;
  static void init()
  {
    if(QueryPerformanceFrequency(&frequency) == 0)
      throw std::logic_error("QueryPerformanceCounter not supported: " + std::to_string(GetLastError()));
  }
};

对于Linux,它甚至更容易。只需阅读的手册页clock_gettime并修改上面的代码即可。


19
MS的标准库维护者已将VC ++ 2012实现确认为错误。
ildjarn

5
对于那些感兴趣的人,这是该错误的链接
Ben Voigt

1
Boost使用QueryPerformanceCounter,因此使用boost :: chrono是解决此错误的好方法,直到发布Visual Studio 14
Mohamed El-Nakib 2014年



2

也许最显着的区别是,其起点std::chrono:system_clock是1.1.1970,即所谓的UNIX-epoch。另一方面,std::chrono::steady_clock通常是PC的启动时间,它最适合用于测量间隔。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.