std::chrono::clock,硬件时钟和周期计数

Vin*_*ent 5 c++ cpu time benchmarking c++-chrono

std::chrono提供几个时钟来测量时间。同时,我猜 cpu 评估时间的唯一方法是计数周期。

问题 1: cpu 或 gpu 是否可以通过计数周期来评估时间?

如果是这样的话,因为计算机计数周期的方式永远不会像原子钟那样精确,这意味着period = std::ratio<1>计算机的“秒”()实际上可能比实际秒更短或更大,从而导致在长时间运行计算机时钟和 GPS 之间的时间测量。

问题2:正确吗?

某些硬件具有不同的频率(例如空闲模式和 Turbo 模式)。在这种情况下,这意味着循环数将在一秒钟内发生变化。

问题 3: cpu 和 gpus 测量的“周期数”是否因硬件频率而异?如果是,那么如何std::chrono处理?如果不是,一个周期对应什么(比如什么是“基本”时间)?有没有办法在编译时访问转换?有没有办法在运行时访问转换?

Pet*_*des 6

计算周期,是的,但是周期是什么

在现代 x86 上,内核使用的时间源(内部以及用于clock_gettime和其他系统调用)通常是计时器中断或偶尔读取的硬件计时器(例如 HPET)。(我实际上不知道细节;当我写这篇文章时,我认为一切都只是基于rdtsc,但我认为这是不正确的。)如果网络可用,通常使用 NTP 来校正比例因子以保持系统时间正确的。

细粒度定时来自固定频率计数器,该计数器对“参考周期”进行计数,而不管涡轮、节能或时钟停止空闲如何。(这是您从 获得的计数器rdtsc,或者__rdtsc()在 C/C++ 中,请参阅此了解更多详细信息,例如,在较旧的 CPU 上,它实际上会计算核心时钟周期,并且在睡眠状态期间不会滴答作响,因此对于 wall- 来说不太有用。时钟时间。)

正常std::chrono实现将使用操作系统提供的函数,例如 POSIX clock_gettime

在 Linux 上,这可以纯粹在用户空间中运行。VDSO页面中的代码+数据被内核映射到每个进程的地址空间。这些数据包括由定时器中断更新的粗略时间戳(CLOCK_REALTIME_COARSE或者CLOCK_MONOTONIC_COARSE我认为直接返回这些时间戳),以及使用 TSC 从系统时钟的最后一个滴答声获取细粒度偏移的偏移量和比例因子。低开销的时间源很好。避免用户->内核->用户往返有很大帮助,在启用 Meltdown + Spectre 缓解措施的情况下更是如此,因为这使得真正的系统调用更加昂贵。

分析不受内存限制的紧密循环可能需要使用实际的核心时钟周期,因此它将对当前核心的实际速度不敏感。(并且不必担心将 CPU 提升到最大睿频等)例如使用perf stat ./a.outperf record ./a.out。例如x86的MOV真的可以“免费”吗?为什么我根本无法重现这个?


有些系统没有/没有直接在 CPU 中内置的挂钟等效计数器,因此您只有粗略的可用时间,并在计时器中断时在 RAM 中更新。或者时间查询功能将从单独的芯片读取时间,可能具有高精度。

rdtsc(系统调用 + 硬件 I/O = 更高的开销,这是 x86 的指令从分析事物转变为时钟源事物的部分原因。)

所有这些时钟频率最终都来自主板上的晶体振荡器。 但是,可以调整从周期计数推断时间的比例因子,以保持时钟与原子时间同步,通常使用网络时间协议(NTP),正如 @Tony 指出的那样。