使用 RDTSC 获取 CPU 周期-为什么 RDTSC 的值总是增加?
- 2024-11-11 08:26:00
- admin 原创
- 19
问题描述:
我想获取特定点的 CPU 周期。我在该点使用此函数:
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
// broken for 64-bit builds; don't copy this code
return x;
}
(编者注:"=A"
对于 x86-64 来说是错误的;它要么选择RDX,要么选择 RAX。只有在 32 位模式下,它才会选择您想要的 EDX:EAX 输出。请参阅如何从 C++ 获取 x86_64 中的 CPU 周期数?。)
问题是它总是返回一个不断增加的数字(每次运行)。就好像它指的是绝对时间。
我是否错误地使用了这些功能?
解决方案 1:
只要您的线程停留在同一个 CPU 核心上,RDTSC 指令就会不断返回一个不断增加的数字,直到它回绕。对于 2GHz CPU,这种情况会在 292 年后发生,因此这不是一个真正的问题。您可能不会看到它发生。如果您希望使用这么长时间,请确保您的计算机每 50 年重新启动一次。
RDTSC 的问题在于,您无法保证它在老款多核 CPU 的所有核心上都在同一时间点启动,也无法保证它在老款多 CPU 主板的所有 CPU 上都在同一时间点启动。
现代系统通常不会出现此类问题,但在老款系统上也可以通过设置线程的亲和性使其仅在一个 CPU 上运行来解决此问题。这对应用程序性能不利,因此通常不应这样做,但对于测量滴答声来说,这样做就没问题了。
(另一个“问题”是许多人使用 RDTSC 来测量时间,但这不是它的作用,但你写道你想要 CPU 周期,所以这没问题。如果你确实使用 RDTSC 来测量时间,当省电或超级加速或任何称为多种频率变化的技术启动时,你可能会感到惊讶。对于实际时间,系统clock_gettime
调用在 Linux 下出奇的好。)
我只想rdtsc
在asm
语句中写入,这对我来说很好用,而且比一些晦涩难懂的十六进制代码更易读。假设它是正确的十六进制代码(因为它既不会崩溃也不会返回不断增加的数字,所以看起来是这样),你的代码是好的。
如果您想要测量一段代码所用的滴答数,您想要滴答差,您只需要减去不断增加的计数器的两个值。类似于uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0;
请注意,如果需要与周围代码隔离的非常精确的测量,则需要在调用之前进行序列化,即停止管道rdtsc
(或使用rdtscp
仅在较新的处理器上受支持的)。可以在每个权限级别使用的序列化指令是cpuid
。
回复评论中的进一步问题:
当您打开计算机时,TSC 从零开始(并且 BIOS 将所有 CPU 上的所有计数器重置为相同的值,尽管几年前的一些 BIOS 不能可靠地做到这一点)。
因此,从程序的角度来看,计数器在“过去的某个未知时间”开始,并且它总是随着 CPU 看到的每个时钟滴答而增加。因此,如果您现在和稍后在不同的进程中执行返回该计数器的指令,它将返回更大的值(除非 CPU 在此期间被暂停或关闭)。同一程序的不同运行会得到更大的数字,因为计数器一直在增长。总是如此。
现在,clock_gettime(CLOCK_PROCESS_CPUTIME_ID)
情况就不同了。这是操作系统分配给进程的 CPU 时间。进程启动时,它从零开始。新进程也从零开始。因此,两个相继运行的进程将获得非常相似或相同的数字,而不是不断增长的数字。
clock_gettime(CLOCK_MONOTONIC_RAW)
更接近于 RDTSC 的工作方式(并且在某些较旧的系统中使用它来实现)。它返回一个不断增加的值。如今,这通常是 HPET。但是,这实际上是时间,而不是滴答声。如果您的计算机进入低功耗状态(例如以正常频率的 1/2 运行),它仍将以相同的速度前进。
解决方案 2:
关于 TSC 有很多令人困惑和/或错误的信息,所以我想尝试澄清其中的一些。
当英特尔首次引入 TSC(在原始奔腾 CPU 中)时,有明确的文档记录了它用于计算周期(而不是时间)。然而,当时 CPU 大多以固定频率运行,因此有些人忽略了文档记录的行为,而是用它来测量时间(最著名的是 Linux 内核开发人员)。他们的代码在后来的 CPU 中失效,这些 CPU 不以固定频率运行(由于电源管理等)。大约在那个时候,其他 CPU 制造商(AMD、Cyrix、Transmeta 等)感到困惑,一些制造商实施 TSC 来测量周期,一些制造商实施它来测量时间,一些制造商使其可配置(通过 MSR)。
随后,“多芯片”系统在服务器中变得越来越普遍;甚至后来还引入了多核。这导致不同核心上的 TSC 值之间出现细微差异(由于启动时间不同);但更重要的是,它还导致不同 CPU 上的 TSC 值出现巨大差异,这是由于 CPU 以不同的速度运行(由于电源管理和/或其他因素)。
从一开始就试图错误地使用它的人(用它来测量时间而不是周期的人)抱怨很多,并最终说服 CPU 制造商将 TSC 标准化为测量时间而不是周期。
当然,这是一团糟 - 例如,如果您支持所有 80x86 CPU,则需要大量代码才能确定 TSC 实际测量的内容;并且不同的电源管理技术(包括 SpeedStep 之类的东西,也包括睡眠状态之类的东西)可能会在不同的 CPU 上以不同的方式影响 TSC;因此 AMD 在 CPUID 中引入了一个“TSC 不变”标志,以告诉操作系统 TSC 可用于正确测量时间。
所有最新的 Intel 和 AMD CPU 都已经这样一段时间了 - TSC 计算时间,根本不测量周期。这意味着如果你想测量周期,你必须使用(特定型号的)性能监控计数器。不幸的是,性能监控计数器甚至更乱(由于其特定型号的性质和复杂的配置)。
解决方案 3:
已经有很好的答案了,Damon 已经在他的回答中提到了这一点,但我将从实际的 x86 手册(第 2 卷,4-301)中为 RDTSC 添加这一点:
将处理器的时间戳计数器(64 位 MSR)的当前值加载到 EDX:EAX 寄存器中。EDX 寄存器加载 MSR 的高 32 位,EAX 寄存器加载低 32 位。(在支持 Intel 64 架构的处理器上,RAX 和 RDX 的高 32 位均被清除。)
处理器在每个时钟周期内单调递增时间戳计数器 MSR,并在处理器重置时将其重置为 0。有关时间戳计数器行为的具体详细信息,请参阅《英特尔® 64 和 IA-32 架构软件开发人员手册》第 3B 卷第 17 章中的“时间戳计数器” 。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件