Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 使用RDTSC获取cpu周期-为什么RDTSC的值总是增加?_Linux_Assembly_X86_Cpu Usage_Rdtsc - Fatal编程技术网

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

Linux 使用RDTSC获取cpu周期-为什么RDTSC的值总是增加?,linux,assembly,x86,cpu-usage,rdtsc,Linux,Assembly,X86,Cpu Usage,Rdtsc,我想得到特定点的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是错

我想得到特定点的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输出。请参阅。)

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


我是否错误地使用了这些函数?

只要线程保持在同一个CPU内核上,RDTSC指令将不断返回一个递增的数字,直到结束。对于2GHz的CPU,这种情况发生在292年后,因此这不是一个真正的问题。你可能不会看到它发生。如果你希望活那么久,确保你的电脑重新启动,比如说,每50年一次

RDTSC的问题在于,您不能保证它在旧多核CPU的所有内核上的同一时间点启动,也不能保证它在旧多CPU板上的所有CPU上的同一时间点启动。
现代系统通常不存在这样的问题,但通过设置线程的关联性,使其仅在一个CPU上运行,也可以在较旧的系统上解决这个问题。这对应用程序性能不好,所以通常不应该这样做,但对于测量滴答声来说,这很好

(另一个“问题”很多人使用RDTSC来测量时间,这并不是它的功能,但你写道你想要CPU周期,所以这很好。如果你真的使用RDTSC来测量时间,当省电、超高功率或任何被称为频率变化的技术出现时,你可能会有惊喜。对于实际时间,
时钟\u gettime系统调用在Linux下出人意料地好。)

我只需要在
asm
语句中编写
rdtsc
,这对我来说很好,并且比一些晦涩的十六进制代码可读性更好。假设它是正确的十六进制代码(因为它既不会崩溃也不会返回不断增加的数字,看起来是这样),那么您的代码就很好了

如果您想要测量一段代码的刻度数,您想要一个刻度差,您只需要减去不断增加的计数器的两个值。类似于
uint64\u t t0=rdtsc()。。。uint64_t t1=rdtsc()-t0
请注意,如果需要与周围代码隔离的非常精确的测量,则在调用
rdtsc
(或使用仅在较新处理器上支持的
rdtscp
)之前,需要序列化,即暂停管道。可以在每个特权级别使用的序列化指令是
cpuid

回复评论中的进一步问题:

当您打开计算机时,TSC从零开始(BIOS会将所有CPU上的所有计数器重置为相同的值,尽管几年前一些BIOSE没有可靠地这样做)

因此,从程序的角度来看,计数器开始于“过去的某个未知时间”,并且它总是随着CPU看到的每个时钟滴答声而增加。因此,如果您现在和以后在不同的进程中执行返回该计数器的指令,它将返回一个更大的值(除非CPU在此期间被挂起或关闭)。同一程序的不同运行得到更大的数值,因为计数器不断增长。总是

现在,
clock\u gettime(clock\u PROCESS\u CPUTIME\u ID)
是另一回事。这是操作系统给予进程的CPU时间。当进程开始时,它从零开始。一个新的过程也从零开始。因此,两个进程在彼此之后运行将得到非常相似或相同的数字,而不是不断增长的数字


clock\u gettime(clock\u MONOTONIC\u RAW)
更接近RDTSC的工作方式(在一些较旧的系统上使用它实现)。它返回一个不断增加的值。现在,这是典型的HPET。然而,这确实是时间,而不是滴答声。如果您的计算机进入低功耗状态(例如,以1/2的正常频率运行),它仍将以相同的速度运行。

关于TSC,有很多令人困惑和/或错误的信息,因此我想我应该尝试清除其中的一些信息

当英特尔首次推出TSC(在最初的奔腾CPU中)时,它清楚地记录了计数周期(而不是时间)。然而,当时的CPU大多以固定的频率运行,因此一些人忽略了记录的行为,而是用它来测量时间(最明显的是Linux内核开发人员)。他们的代码在后来的CPU中被破解,这些CPU不能以固定的频率运行(由于电源管理等原因)。当时,其他CPU制造商(AMD、Cyrix、Transmeta等)感到困惑,一些实施TSC以测量周期,一些实施TSC以测量时间,还有一些使其可配置(通过MSR)

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

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

当然,这是一个混乱-例如,如果您支持所有80x86 CPU,则需要大量代码才能确定TSC实际测量的内容;不同的电源管理技术(包括SpeedStep之类的技术,也包括睡眠状态之类的技术)可能会以不同的方式在不同的CPU上影响TSC;因此AMD在CPUID中引入了一个“TSC不变量”标志,告诉操作系统TSC可以用于meas