使用RDTSC获取cpu周期 - 为什么RDTSC的值总是增加?

use*_*106 16 linux x86 assembly cpu-usage

我想在特定点获得CPU周期.我在这一点上使用这个功能:

static __inline__ unsigned long long rdtsc(void)
{
    unsigned long long int x;
    __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
    return x;
}
Run Code Online (Sandbox Code Playgroud)

问题是它总是返回一个增加的数字(在每次运行中).就好像它指的是绝对时间.

我错误地使用了这些功能吗?

Dam*_*mon 27

只要你的线程停留在同一个CPU内核上,RDTSC指令将继续返回一个增加的数字,直到它回绕.对于2GHz CPU,这发生在292年后,所以这不是一个真正的问题.你可能不会看到它发生.如果您希望活得那么久,请确保您的计算机每50年重新启动一次.

RDTSC的问题在于,您无法保证它在老式多核CPU的所有内核上的相同时间点启动,并且无法保证它在老式多CPU板上的所有CPU上的相同时间点启动.
现代系统通常没有这样的问题,但是通过设置线程的亲和性,问题也可以在旧系统上解决,因此它只能在一个CPU上运行.这对应用程序性能不好,所以通常不应该这样做,但是对于测量滴答,它就好了.

(另一个"问题"是许多人使用RDTSC来测量时间,这不是它的作用,但你写道你想要CPU周期,所以没关系.如果你确实使用RDTSC来测量时间,你可能会有惊喜无论是节电还是超级增压,或者无论多少频率变化技术都被称为踢入.实际上,clock_gettime在Linux下,系统调用非常好.)

我只是rdtscasm声明中写,这对我来说效果很好,并且比一些模糊的十六进制代码更具可读性.假设它是正确的十六进制代码(因为它既没有崩溃也没有返回一个不断增加的数字,似乎是这样),你的代码很好.

如果你想测量一段代码所需的刻度数,你需要一个刻度,你只需要减去不断增加的计数器的两个值.喜欢的东西,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正常频率运行),它仍将以相同的速度前进.

  • 这帮助我消除了上面评论中的困惑:/sf/answers/774243361/ (3认同)
  • **在现代CPU上,RDTSC*在参考周期内测量时间**.在CPU上,其中包括CPUID和tsc_invariant nonstop_tsc的`gettimeofday`系统调用**是在用户空间(VDSO页)实施了RDTSC方面.(我假设某些clk_id值是`clock_gettime`).CPU厂商决定,有一个非常低的开销TIMESOURCE比有RDTSC作为基准工具的更有价值,所以他们改变了它,你将有来自〜2005年的CPU问题(?)以后,如果你*想*测用它循环.但是你可以使用性能计数器. (2认同)

Bre*_*dan 20

有很多关于TSC的令人困惑和/或错误的信息,所以我想我会尝试清除其中的一些.

当英特尔首次推出TSC(在最初的Pentium CPU中)时,可以清楚地记录计算周期(而不是时间).然而,当时CPU主要以固定频率运行,因此有些人忽略了记录的行为并用它来测量时间(最值得注意的是,Linux内核开发人员).他们的代码破坏了后来的CPU,这些CPU没有以固定频率运行(由于电源管理等).大约在那个时候,其他CPU制造商(AMD,Cyrix,Transmeta等)感到困惑,一些实施TSC来测量周期,一些实施它以便测量时间,一些使其可配置(通过MSR).

然后,"多芯片"系统在服务器上变得越来越普遍; 甚至后来的多核心被引入.这导致不同核心上的TSC值之间的微小差异(由于启动时间不同); 但更重要的是,它还导致CPU以不同的速度运行(由于电源管理和/或其他因素)导致不同CPU上的TSC值之间的主要差异.

试图从一开始就错误地使用它的人(使用它来测量时间而不是周期的人)抱怨很多,最终说服CPU制造商标准化TSC测量时间而不是周期.

当然这是一团糟 - 例如,如果您支持所有80x86 CPU,那么只需要确定TSC实际测量的内容需要大量代码; 不同的电源管理技术(包括SpeedStep之类的东西,还有睡眠状态之类的东西)可能会在不同的CPU上以不同的方式影响TSC; 所以AMD在CPUID中引入了一个"TSC不变"标志,告诉操作系统可以使用TSC正确测量时间.

所有最近的Intel和AMD CPU都已经有一段时间了 - TSC计算时间并且根本不测量周期.这意味着如果要测量必须使用的(特定于模型的)性能监视计数器的周期.不幸的是,性能监控计数器更糟糕(由于它们的模型特定性质和复杂的配置).

  • 有趣的是,它以“参考周期”计算时间,并以 CPU 的“额定”时钟速度运行(即,如果它以 2.4GHz CPU 出售,则为 RDTSC 计数频率)。要测量核心时钟周期,请使用性能计数器来测量“unhalted_core_cycles”或其他东西。 (2认同)