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
处理?如果不是,一个周期对应什么(比如什么是“基本”时间)?有没有办法在编译时访问转换?有没有办法在运行时访问转换?
计算周期,是的,但是周期是什么?
在现代 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.out
或perf record ./a.out
。例如x86的MOV真的可以“免费”吗?为什么我根本无法重现这个?
有些系统没有/没有直接在 CPU 中内置的挂钟等效计数器,因此您只有粗略的可用时间,并在计时器中断时在 RAM 中更新。或者时间查询功能将从单独的芯片读取时间,可能具有高精度。
rdtsc
(系统调用 + 硬件 I/O = 更高的开销,这是 x86 的指令从分析事物转变为时钟源事物的部分原因。)
所有这些时钟频率最终都来自主板上的晶体振荡器。 但是,可以调整从周期计数推断时间的比例因子,以保持时钟与原子时间同步,通常使用网络时间协议(NTP),正如 @Tony 指出的那样。