具有直接 QueryPerformanceCounter 值的 Clock 是否符合 C++ 标准?

Can Clock with direct QueryPerformanceCounter value be conforming to C++ Standard?

假设我想创建 Clock 直接 QueryPerformanceCounter Windows API 结果。 QueryPerformanceCounter Windows API returns 一些计数器应该除以 QueryPerformanceFrequency 结果,从而产生以秒为单位的时间。

通常基于QueryPerformanceCounterClock会通过乘以某个周期并除以QueryPerformanceFrequency立即将结果转换为某些单位。这就是 steady_clock 在 Windows 上的实现方式。

但是假设出于性能原因我想在真正需要之前避免除法。所以time_point是直接的QueryPerformanceCounter值,duration是这样的差值。而且我大部分时间都可以对这些值进行算术和比较,仅将最终结果转换为一些正常的 durationtime_point

我相信这是可能的。我的问题是:这样的 Clock 会完全兼容标准 Clock.

它不会与标准 Clock Requirements 完全兼容。但大多数时候它会编译并做你想做的事。不符合的部分是您必须为 time_point 所基于的 period 指定 something。而且某些东西不一定对应于物理时间单位。

这并不重要,除非您减去其中两个 time_points,得到一个 duration,然后将 duration 做的事情进行比较代表物理时间。然后你会得到 运行 次垃圾。

此外,如果您在 sleep_untilwait_until 中使用这样的 time_point,那么您的程序将不会休眠或等待预期的时间。

这是一个基于 QueryPerformanceCounter 的计时时钟示例,它确实用 QueryPerformanceFrequency 确定了物理单位:

重新定义 time_pointduration 的时钟不符合编译时已知的 period 不符合 Clock 要求,正如 Howard Hinnant 解释的那样。


使用 a class 模拟算术类型 来隐藏额外的实现细节是行不通的。

由于标准没有定义什么是模拟,使用模拟类型的机会仅适用于标准实现提供的时钟。作为实际证明,std::chronoboost::chrono 不能很好地与 boost::rational 一起使用。


但是,避免每次查询出现分裂 的一般任务至少有两个实用的解决方案。

第一个解决方案是遵循将除法延迟到需要时的意图,而不是重新定义 time_pointduration。定义符合标准的时钟,并且作为扩展,定义原始时间戳的接口。所以,时钟可能有 raw_now() 方法,返回 raw_time_point 类型,这样的区别是 raw_duration 类型。 raw_time_point 可以隐式转换为 time_point 并且 raw_duration 可以隐式转换为 duration.


第二种解决方案是为每次查询计算符合标准的time_point,但是使用libdivide库执行快速除法而不进行除法运算。

该库采用除数并对其进行处理,以便使用经过处理的除数的每个除法都使用快速操作完成。当然,处理除数需要时间,但对于同一个除数的重复除法,这是一次性的开销。所以这个库看起来很适合这个任务。 (请注意,该库对于编译时已知的常量没有用,因为在这种情况下,编译器会避免自行除法,但 QueryPerformanceFrequency 结果是 运行-时间常数)。