8
#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
4

5 回答 5

11

我已经在运行在不同 Intel CPU 上的几个 Linux 发行版上尝试了你的代码(诚然,所有这些都比你似乎正在使用的 Pentium 4 HT 630 更新)。在所有这些测试中,我得到了 25 到 50 个周期之间的值。

我唯一与所有证据一致的假设是,您在虚拟机内而不是在裸机上运行操作系统,并且 TSC 正在虚拟化。

于 2011-11-30T10:50:33.917 回答
7

获得大量数据的原因有很多:

  • 操作系统进行了上下文切换,您的进程进入睡眠状态。
  • 发生磁盘寻道,您的进程进入睡眠状态。
  • ......关于您的流程为何会被忽略的任何原因。

请注意,这rdtsc对于没有工作的计时并不是特别可靠,因为:

  • 处理器速度可能会发生变化,因此周期的长度(以秒为单位)会发生变化。
  • 对于给定的瞬间,不同的处理器可能具有不同的 TSC 值。

大多数操作系统都有高精度的时钟或计时方法。clock_gettime例如在 Linux 上,尤其是单调时钟。(也要了解挂钟和单调时钟之间的区别:挂钟可以向后移动——即使在 UTC 中也是如此。)在 Windows 上,我认为建议是QueryHighPerformanceCounter. 通常,这些时钟提供的精度足以满足大多数需求。


此外,查看程序集,您似乎只得到了 32 位的答案:我看不到%edxrdtsc.


运行您的代码,我得到 120-150 ns 的clock_gettime使用时间CLOCK_MONOTONIC和 70-90 个周期的 rdtsc (全速约 20 ns,但我怀疑处理器的时钟频率下降了,实际上大约是 50 ns)。(在笔记本电脑桌面上(该死的 SSH,忘记了我在哪台机器上!)大约持续 20% 的 CPU 使用率)确定你的机器没有陷入困境吗?

于 2011-11-30T09:31:14.243 回答
4

看起来您的操作系统禁用了用户空间中 RDTSC 的执行。而且您的应用程序必须切换到内核并返回,这需要很多周期。

这来自英特尔软件开发人员手册:

在受保护或虚拟 8086 模式下,寄存器 CR4 中的时间戳禁用 (TSD) 标志限制 RDTSC 指令的使用,如下所示。当 TSD 标志清零时,RDTSC 指令可以在任何特权级执行;当标志被设置时,指令只能在特权级 0 下执行。(在实地址模式下,RDTSC 指令总是被启用。)

编辑:

回答 aix 的评论,我解释了为什么 TSD 最有可能是这里的原因。

我只知道程序执行一条指令的时间比平时长的这些可能性:

  1. 在一些模拟器下运行,
  2. 使用自修改代码,
  3. 上下文切换,
  4. 内核切换。

前两个原因通常不能将执行延迟超过几百个周期。2000-2500 周期对于上下文/内核切换更为典型。但实际上不可能在同一个地方多次捕获上下文切换。所以应该是内核开关。这意味着任何一个程序都在调试器下运行,或者在用户模式下不允许使用 RDTSC。

操作系统禁用 RDTSC 的最可能原因可能是安全性。有人尝试使用 RDTSC 来破解加密程序。

于 2011-11-30T09:46:39.460 回答
1

指令缓存未命中?(这是我的猜测)

还有,可能,

在虚拟化系统中切换到管理程序?程序引导程序的残余(包括同一 CPU 上的网络活动)?

致 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;
}
于 2012-10-31T05:05:37.533 回答
0

只是一个想法-也许这两个 rdtsc 指令在不同的内核上执行?rdtsc 值可能会因内核而略有不同。

于 2011-11-30T09:36:07.480 回答