一尘不染

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

linux

我想在特定时间获取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输出中看到的。

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

我使用的功能不正确吗?


阅读 395

收藏
2020-06-02

共1个答案

一尘不染

只要您的线程停留在相同的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的工作方式(并且在某些旧系统上是用RDTSC实现的)。它返回一个不断增加的值。如今,这通常是HPET。但是,这实际上是
时间 ,而不是 滴答滴答 。如果您的计算机进入低功耗状态(例如,以1/2正常频率运行),它 仍将 以相同的速度前进。

2020-06-02