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

Difference between std::system_clock and std::steady_clock?

本文关键字:std clock 区别 system steady 之间      更新时间:2023-10-16

std::system_clockstd::steady_clock有什么区别?(说明不同结果/行为的示例案例会很棒)。

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

从 N3376

:20.11.7.1 [时间-时钟.系统]/1:

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

20.11.7.2 [时间.时钟.稳定]/1:

steady_clock的对象表示时钟,其time_point值永远不会随着物理时间的推进而减少,并且time_point的值相对于实时以稳定的速率前进。也就是说,时钟可能无法调整。

20.11.7.3 [时间.时钟.雇用]/1:

high_resolution_clock的对象表示时钟周期最短的时钟。high_resolution_clock可能是system_clocksteady_clock的同义词。

例如,系统范围的时钟可能会受到夏令时等因素的影响,此时未来某个时间点列出的实际时间实际上可能是过去的时间。(例如,在美国,在秋季时间向后移动一小时,因此同一小时会经历"两次")但是,steady_clock不允许受到这些事情的影响。

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

在表 59 中,C1C2表示时钟类型。t1t2C1::now()返回的值,其中呼叫返回t1发生在呼叫返回t2之前,并且这两个调用都发生在C1::time_point::max()之前。[ 注意:这意味着C1没有在t1t2之间环绕。

表达式:C1::is_steady
返回:const bool
操作语义:truet1 <= t2是否始终为真且时钟周期之间的时间恒定,否则false

这就是关于它们差异的所有标准。

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

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

海湾合作委员会 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的手册页并修改上面的代码即可。

GCC 5.3.0 实现

C++ stdlib 在 GCC 源代码中:

  • high_resolution_clocksystem_clock的别名
  • system_clock转发到以下第一个可用的:
    • clock_gettime(CLOCK_REALTIME, ...)
    • gettimeofday
    • time
  • steady_clock转发到以下第一个可用的选项:
    • clock_gettime(CLOCK_MONOTONIC, ...)
    • system_clock

然后CLOCK_REALTIMECLOCK_MONOTONIC解释在:CLOCK_REALTIME和CLOCK_MONOTONIC之间的区别?

霍华德·欣南特(Howard Hinnant)关于时间的相关讨论,chrono的作者:

不要使用high_resolution_clock,因为它是其中之一的别名:

  • system_clock:它就像一个普通的时钟,用于时间/日期相关的东西
  • steady_clock:它就像一个秒表,用它来计时。

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