#include <stdio.h>
static inline unsigned long long tick()
{
unsigned long long d;
__asm__ __volatile__ ("rdtsc" : "=A" (d) );
return d;
}
int main()
{
long long res;
res=tick();
res=tick()-res;
printf("%d",res);
return 0;
}
我用gcc编译了这段代码-O0 -O1 -O2 -O3优化。我总是得到2000-2500个周期。任何人都可以解释这个输出的原因吗?如何度过这些周期?
第一个函数“tick”错误。这是对的。
另一个版本的函数“tick”
static __inline__ unsigned long long tick()
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
这是-O3
的汇编代码 .file "rdtsc.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
subl $40, %esp
movl %ecx, -16(%ebp)
movl %ebx, -12(%ebp)
movl %esi, -8(%ebp)
movl %edi, -4(%ebp)
#APP
# 6 "rdtsc.c" 1
rdtsc
# 0 "" 2
#NO_APP
movl %edx, %edi
movl %eax, %esi
#APP
# 6 "rdtsc.c" 1
rdtsc
# 0 "" 2
#NO_APP
movl %eax, %ecx
movl %edx, %ebx
subl %esi, %ecx
sbbl %edi, %ebx
movl %ecx, 4(%esp)
movl %ebx, 8(%esp)
movl $.LC0, (%esp)
call printf
movl -16(%ebp), %ecx
xorl %eax, %eax
movl -12(%ebp), %ebx
movl -8(%ebp), %esi
movl -4(%ebp), %edi
movl %ebp, %esp
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Debian 4.3.2-1.1) 4.3.2"
.section .note.GNU-stack,"",@progbits
这是CPU
processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 4
model name : Intel(R) Xeon(TM) CPU 3.00GHz
stepping : 3
cpu MHz : 3000.105
cache size : 2048 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 5
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss constant_tsc up pebs bts pni
bogomips : 6036.62
clflush size : 64
答案 0 :(得分:10)
我已经在不同英特尔CPU上运行的几个Linux发行版上尝试过你的代码(不可否认,它们比你似乎使用的Pentium 4 HT 630更新)。在所有这些测试中,我得到的值在25到50个周期之间。
我唯一的假设是,与所有证据一致的是,您在虚拟机内运行操作系统而不是裸机,并且TSC正在虚拟化。
答案 1 :(得分:7)
有很多理由可以获得大量数据:
请注意,rdtsc
对于没有工作的时间安排并不是特别可靠,因为:
大多数操作系统都具有高精度时钟或定时方法。例如,Linux上的clock_gettime
,特别是单调时钟。 (了解挂钟和单调时钟之间的区别:挂钟可以向后移动 - 即使是UTC。)在Windows上,我认为建议是QueryHighPerformanceCounter
。通常,这些时钟可为大多数需求提供足够的精度。
另外,看一下这个程序集,看起来你得到的答案只有32位:%edx
后我没有看到rdtsc
被保存。
运行代码,clock_gettime
使用CLOCK_MONOTONIC
获得120-150 ns的时序,rdtsc获得70-90个周期(全速约20 ns,但我怀疑处理器是时钟控制的)下来,这真的大约50 ns)。 (在一个笔记本电脑桌面上(忘了SSH,忘了我在哪台机器上!),CPU占用率一直在20%左右)当然你的机器没有陷入困境?
答案 2 :(得分:4)
看起来您的操作系统禁用了用户空间中RDTSC的执行。你的应用程序必须切换到内核并返回,这需要很多周期。
这来自英特尔软件开发人员手册:
当处于受保护或虚拟8086模式时,时间戳禁用(TSD)标志位于 寄存器CR4限制使用RDTSC指令,如下所示。当TSD标志 很清楚,RDTSC指令可以在任何权限级别执行;当国旗 如果设置,该指令只能在特权级别0执行。(在实地址时 模式,始终启用RDTSC指令。)
修改强>
回答aix的评论,我解释一下,为什么TSD很可能就是这里的原因。
我只知道程序执行单个指令的可能性比平常更长:
前两个原因通常不能延迟执行超过几百个周期。对于上下文/内核切换,2000-2500个周期更为典型。但实际上不可能在同一个地方多次捕获上下文切换。所以它应该是内核切换。这意味着程序在调试器下运行,或者在用户模式下不允许RDTSC。
操作系统禁用RDTSC的最可能原因可能是安全性。有人试图使用RDTSC破解加密程序。
答案 3 :(得分:1)
指令缓存未命中? (这是我的猜测)
另外,可能
切换到虚拟化系统中的虚拟机管理程序? 程序引导程序的遗留物(包括同一CPU上的网络活动)?
To Thanatos:在比2008年更近的系统上,rdtsc()是一个挂钟,不随频率步长而变化。
你可以试试这个小代码吗?
int main()
{
long long res;
fflush(stdout); // chnage the exact timing of stdout, in case there is something to write in a ssh connection, together with its interrupts
for (int pass = 0; pass < 2; pass++)
{
res=tick();
res=tick()-res;
}
printf("%d",res); // ignore result on first pass, display the result on second pass.
return 0;
}
答案 4 :(得分:0)
只是一个想法 - 也许这两个rdtsc指令在不同的核心上执行? rdtsc值可能会因核心而略有不同。