在我的 E8200 机器上,这不会发生,但在我的 Atom N450 上网本(都运行 OpenSuse 11.2)上,每当我读取 CPU 的 TSC 时,返回的值为mod 10 == 0
,即它不能被 10 整除。我正在使用 RDTSC测量有趣的代码所花费的时间的价值,但为了演示的目的,我编写了这个小程序:
.text
.global _start
_start: xorl %ebx,%ebx
xorl %ecx,%ecx
xorl %r14d,%r14d
movb $10,%cl
loop: xchgq %rcx,%r15 # save to reg
cpuid
rdtsc
shlq $32,%rdx
xorq %rax,%rdx # full 64 bit of RDTSC
movq %r14,%r13 # save the old value
movq %rdx,%r14 # copy current
movq %r14,%rsi # argv[1] of printf()
subq %r13,%rdx # argv[2] (delta)
leaq format(%rip),%rdi # argv[0]
xorl %eax,%eax # no stack varargs
call printf
xchgq %rcx,%r15
loop loop
0: xorl %eax,%eax
movb $0x3c,%al
syscall
.size _start, .-_start
.data
format: .asciz "rdtsc: %#018llx = %1$llu -- delta: %llu\n"
(我通常使用自己的例程进行转换,但为了防止读者暗示可能存在错误,我在这里仅使用 printf() 。)
使用上面的代码,输出是(例如):
rdtsc: 0x000b88ef933ffd06 = 3246787292822790 -- delta: 3246787292822790
rdtsc: 0x000b88ef9342fcf4 = 3246787293019380 -- delta: 196590
rdtsc: 0x000b88ef93435dca = 3246787293044170 -- delta: 24790
rdtsc: 0x000b88ef9343b43c = 3246787293066300 -- delta: 22130
rdtsc: 0x000b88ef93440c34 = 3246787293088820 -- delta: 22520
rdtsc: 0x000b88ef9344604e = 3246787293110350 -- delta: 21530
rdtsc: 0x000b88ef9344b4d6 = 3246787293131990 -- delta: 21640
rdtsc: 0x000b88ef9345085a = 3246787293153370 -- delta: 21380
rdtsc: 0x000b88ef93455d96 = 3246787293175190 -- delta: 21820
rdtsc: 0x000b88ef9345b16a = 3246787293196650 -- delta: 21460
可以很容易地看出,delta 在合理的范围内变化。但显着(不是说合谋;-)是最低有效十进制数字始终为0。
我已经观察到这种现象两年多了,而 Stack Overflow 并不是我公开这个问题的第一个地址。但我还没有得到一个合理的答案。我们(我和其他人)提出的想法是
- TSC 仅每 10个周期增加一次,然后增加 10,或
- TSC 在内部正确更新,但仅每 10个周期反映到外部,或
- TSC 每个周期递增 10。
然而,这些观点都没有真正的意义。我实际上应该在 E8200 上运行一个类似的程序(当前出现故障),以查看增量的数量级是否与上述输出中的数量级相同或只有十分之一。(有志愿者吗?)
谷歌搜索没有帮助,英特尔的手册也没有。
在与其他人讨论时,没有其他人经历过同样的行为。如果它与内核有关,那么至少有 3 个版本受到影响,但是……内核与它有什么关系?
我还使用了上网本,它带有一个新的主板——意味着一个新的 CPU,所以 N450 至少有两个单独的实体必须受到影响。
我还针对时钟频率变化采取了措施(无论我将时钟固定到什么频率,值都只在预期范围内变化(如图所示)),并关闭了 HT,尽管这些实际上应该有助于获得一些其他最低有效数字,而不是阻止它们。但只是为了确定。
好吧,如果有人想在他们的机器上运行程序,命令行是(假设你将源代码保存在文件中rdtsc.s
):
as rdtsc.s -o rdtsc.o
ld --dynamic-linker=/lib64/ld-linux-x86-64.so.2 rdtsc.o -L /lib64 -l c -o rdtsc
为了用 gcc 前端构建它,即
gcc -l c rdtsc.s -o rdtsc
您必须添加(或替换_start:
标签)main:
标签并使其成为全局标签。
[更新(2012-09-15 ~21:15 UTC):实际上我之前也可以这样做:我只是让它在 a 之前和之后取 TSC sleep(1)
,这给出了一个略大于 1,666,000,000 的增量,这表明第三个上面列表中的点是错误的。但我仍然不知道为什么我没有得到完整的精度。/更新]