具有直接 QueryPerformanceCounter 值的 Clock 是否符合 C++ 标准?
Can Clock with direct QueryPerformanceCounter value be conforming to C++ Standard?
假设我想创建 Clock
直接 QueryPerformanceCounter
Windows API 结果。 QueryPerformanceCounter
Windows API returns 一些计数器应该除以 QueryPerformanceFrequency
结果,从而产生以秒为单位的时间。
通常基于QueryPerformanceCounter
的Clock
会通过乘以某个周期并除以QueryPerformanceFrequency
立即将结果转换为某些单位。这就是 steady_clock
在 Windows 上的实现方式。
但是假设出于性能原因我想在真正需要之前避免除法。所以time_point
是直接的QueryPerformanceCounter
值,duration
是这样的差值。而且我大部分时间都可以对这些值进行算术和比较,仅将最终结果转换为一些正常的 duration
或 time_point
。
我相信这是可能的。我的问题是:这样的 Clock
会完全兼容标准 Clock
.
它不会与标准 Clock Requirements 完全兼容。但大多数时候它会编译并做你想做的事。不符合的部分是您必须为 time_point
所基于的 period
指定 something。而且某些东西不一定对应于物理时间单位。
这并不重要,除非您减去其中两个 time_points,得到一个 duration
,然后将 duration
与 做的事情进行比较代表物理时间。然后你会得到 运行 次垃圾。
此外,如果您在 sleep_until
或 wait_until
中使用这样的 time_point
,那么您的程序将不会休眠或等待预期的时间。
这是一个基于 QueryPerformanceCounter
的计时时钟示例,它确实用 QueryPerformanceFrequency
确定了物理单位:
重新定义 time_point
和 duration
的时钟不符合编译时已知的 period
不符合 Clock
要求,正如 Howard Hinnant 解释的那样。
使用 a class 模拟算术类型 来隐藏额外的实现细节是行不通的。
由于标准没有定义什么是模拟,使用模拟类型的机会仅适用于标准实现提供的时钟。作为实际证明,std::chrono
和 boost::chrono
不能很好地与 boost::rational
一起使用。
但是,避免每次查询出现分裂 的一般任务至少有两个实用的解决方案。
第一个解决方案是遵循将除法延迟到需要时的意图,而不是重新定义 time_point
和 duration
。定义符合标准的时钟,并且作为扩展,定义原始时间戳的接口。所以,时钟可能有 raw_now()
方法,返回 raw_time_point
类型,这样的区别是 raw_duration
类型。 raw_time_point
可以隐式转换为 time_point
并且 raw_duration
可以隐式转换为 duration
.
第二种解决方案是为每次查询计算符合标准的time_point
,但是使用libdivide库执行快速除法而不进行除法运算。
该库采用除数并对其进行处理,以便使用经过处理的除数的每个除法都使用快速操作完成。当然,处理除数需要时间,但对于同一个除数的重复除法,这是一次性的开销。所以这个库看起来很适合这个任务。 (请注意,该库对于编译时已知的常量没有用,因为在这种情况下,编译器会避免自行除法,但 QueryPerformanceFrequency
结果是 运行-时间常数)。
假设我想创建 Clock
直接 QueryPerformanceCounter
Windows API 结果。 QueryPerformanceCounter
Windows API returns 一些计数器应该除以 QueryPerformanceFrequency
结果,从而产生以秒为单位的时间。
通常基于QueryPerformanceCounter
的Clock
会通过乘以某个周期并除以QueryPerformanceFrequency
立即将结果转换为某些单位。这就是 steady_clock
在 Windows 上的实现方式。
但是假设出于性能原因我想在真正需要之前避免除法。所以time_point
是直接的QueryPerformanceCounter
值,duration
是这样的差值。而且我大部分时间都可以对这些值进行算术和比较,仅将最终结果转换为一些正常的 duration
或 time_point
。
我相信这是可能的。我的问题是:这样的 Clock
会完全兼容标准 Clock
.
它不会与标准 Clock Requirements 完全兼容。但大多数时候它会编译并做你想做的事。不符合的部分是您必须为 time_point
所基于的 period
指定 something。而且某些东西不一定对应于物理时间单位。
这并不重要,除非您减去其中两个 time_points,得到一个 duration
,然后将 duration
与 做的事情进行比较代表物理时间。然后你会得到 运行 次垃圾。
此外,如果您在 sleep_until
或 wait_until
中使用这样的 time_point
,那么您的程序将不会休眠或等待预期的时间。
这是一个基于 QueryPerformanceCounter
的计时时钟示例,它确实用 QueryPerformanceFrequency
确定了物理单位:
重新定义 time_point
和 duration
的时钟不符合编译时已知的 period
不符合 Clock
要求,正如 Howard Hinnant 解释的那样。
使用 a class 模拟算术类型 来隐藏额外的实现细节是行不通的。
由于标准没有定义什么是模拟,使用模拟类型的机会仅适用于标准实现提供的时钟。作为实际证明,std::chrono
和 boost::chrono
不能很好地与 boost::rational
一起使用。
但是,避免每次查询出现分裂 的一般任务至少有两个实用的解决方案。
第一个解决方案是遵循将除法延迟到需要时的意图,而不是重新定义 time_point
和 duration
。定义符合标准的时钟,并且作为扩展,定义原始时间戳的接口。所以,时钟可能有 raw_now()
方法,返回 raw_time_point
类型,这样的区别是 raw_duration
类型。 raw_time_point
可以隐式转换为 time_point
并且 raw_duration
可以隐式转换为 duration
.
第二种解决方案是为每次查询计算符合标准的time_point
,但是使用libdivide库执行快速除法而不进行除法运算。
该库采用除数并对其进行处理,以便使用经过处理的除数的每个除法都使用快速操作完成。当然,处理除数需要时间,但对于同一个除数的重复除法,这是一次性的开销。所以这个库看起来很适合这个任务。 (请注意,该库对于编译时已知的常量没有用,因为在这种情况下,编译器会避免自行除法,但 QueryPerformanceFrequency
结果是 运行-时间常数)。