基于rdtsc的gcc优化问题

基于rdtsc的gcc优化问题,c,optimization,gcc,C,Optimization,Gcc,我使用rdtsc和cpuid指令,使用易失性内联汇编指令来测量程序的CPU周期。rdtsc指令为我在Linux上使用speed optimization-o2-fomit帧指针的程序和使用speed optimization options的Windows上的程序提供了现实的结果。我认为它是针对MS Visual Studio 2008的C编译器的VC 9.0 最近,我实现了一个新的程序,它使用了大量的表查找和类似的东西。然而,在Linux上使用gcc优化的这个程序的rdtsc测量值总是导致错误

我使用rdtsc和cpuid指令,使用易失性内联汇编指令来测量程序的CPU周期。rdtsc指令为我在Linux上使用speed optimization-o2-fomit帧指针的程序和使用speed optimization options的Windows上的程序提供了现实的结果。我认为它是针对MS Visual Studio 2008的C编译器的VC 9.0

最近,我实现了一个新的程序,它使用了大量的表查找和类似的东西。然而,在Linux上使用gcc优化的这个程序的rdtsc测量值总是导致错误的测量值——比我预期的CPU周期要少。在使用优化和编译器编译的Windows上运行同一程序时,rdtsc测量结果是真实的,符合预期

我的问题是,gcc优化是否有办法将易失性汇编指令移动到某些地方来产生上述行为

我的计时器代码如下所示:

#define TIMER_VARS                                                 \
  uint32 start_lo, start_hi;                                       \
  uint32 ticks_lo, ticks_hi

#define TIMER_START()                                              \
  __asm__ __volatile__                                             \
     ("rdtsc"                                                      \
     : "=a" (start_lo), "=d" (start_hi) /* a = eax, d = edx*/      \
     : /* no input parameters*/                                    \
     : "%ebx", "%ecx", "memory")

#define TIMER_STOP()                                               \
  __asm__ __volatile__                                             \
     ("rdtsc"                                                      \
     "\n        subl %2, %%eax"                                    \
     "\n        sbbl %3, %%edx"                                    \
     : "=&a" (ticks_lo), "=&d" (ticks_hi)                          \
     : "g" (start_lo), "g" (start_hi)                              \
     : "%ebx", "%ecx", "memory")
如果有人能就此提出一些想法,我将不胜感激


谢谢,

为了防止内联rdtsc函数在任何加载/存储/其他操作之间移动,您都应该将asm写为uu asm uu volatile u u,并将内存包含在clobber列表中。如果不执行后一种操作,GCC将无法删除asm或在可能需要结果或更改asm输入的任何指令之间移动asm,但它仍然可以针对不相关的操作移动asm。内存阻塞意味着GCC不能对内存内容做出任何假设,任何地址可能泄漏的变量在asm中保持不变,因此移动它变得更加困难。但是,GCC可能仍然能够在指令之间移动asm,这些指令只修改地址从未被获取的局部变量,因为它们不是内存


哦,正如wildplasser在一篇评论中所说的,在浪费大量时间之前检查asm输出。

为了防止内联rdtsc函数在任何加载/存储/其他操作中移动,您都应该将asm写为“asm\uuuu volatile”,并将内存包含在clobber列表中。如果不执行后一种操作,GCC将无法删除asm或在可能需要结果或更改asm输入的任何指令之间移动asm,但它仍然可以针对不相关的操作移动asm。内存阻塞意味着GCC不能对内存内容做出任何假设,任何地址可能泄漏的变量在asm中保持不变,因此移动它变得更加困难。但是,GCC可能仍然能够在指令之间移动asm,这些指令只修改地址从未被获取的局部变量,因为它们不是内存


哦,正如wildplasser在评论中所说的,在浪费大量时间之前,请检查asm输出。

我不知道它是否正确,但我曾经使用的代码是:

#define rdtscll(val) \
      __asm__ __volatile__("rdtsc" : "=A" (val))

typedef unsigned unsigned long long Ull;

static inline Ull myget_cycles (void)
{
Ull ret;

rdtscll(ret);
return ret; 
}

我记得英特尔的速度比AMD慢。YMMV.

我不知道它是否正确,但我曾经使用的代码是:

#define rdtscll(val) \
      __asm__ __volatile__("rdtsc" : "=A" (val))

typedef unsigned unsigned long long Ull;

static inline Ull myget_cycles (void)
{
Ull ret;

rdtscll(ret);
return ret; 
}

我记得英特尔的速度比AMD慢。YMMV。

单核和多核机器之间的行为存在差异多核机器有时会同步时钟,tdtsc有时会丢弃缓存或TLB,Intel和AMD之间存在差异,芯片版本之间也存在差异。另外:rdtsc是一条受限制的指令,甚至可能由陷阱处理。我猜VM会做类似的事情,我认为编译器几乎没有影响。请检查编译器程序集输出。在使用英特尔单核处理器的计算机上,我遇到了这个问题。我刚刚添加了计时器的代码。这可能会提供一些进一步的线索。在性能模式下运行时,您是否确保将测试应用程序设置为单线程、与一个内核的关联性以及CPU时钟速度强制设置为最大值(即无自动频率调整)?您不需要%ebx和%ecx clobbers。这些是使用cpuid序列化后留下的吗?你可以用lfence代替。反正你看,;您可以只使用uu rdtsc固有功能。单核和多核机器之间存在行为差异多核机器有时会同步时钟,tdtsc有时会丢弃缓存或TLB,Intel和AMD之间存在差异,芯片版本之间也存在差异。另外:rdtsc是一条受限制的指令,甚至可能由陷阱处理。我猜VM会做类似的事情,我认为编译器几乎没有影响。请检查编译器程序集输出。在使用英特尔单核处理器的计算机上,我遇到了这个问题。我刚刚添加了计时器的代码。这可能会提供一些进一步的线索。在性能模式下运行时,您是否确保将您的测试应用程序设置为单线程、与一个核心的关联以及
CPU时钟速度强制达到最大值,即没有自动频率调整?您不需要%ebx和%ecx缓冲。这些是使用cpuid序列化后留下的吗?你可以用lfence代替。反正你看,;你可以使用u rdtsc的内在特性,我没有想到显式的障碍。还请注意,由于指令障碍,CPU将不得不排空管道,从而导致性能损失。这是一个Heisenticker…是的,这几乎是不可能避免的。是的,在线组装说明是用asm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuvolatile\uuuuuuuuuuu编写的。根据你的建议,我将内存添加到了clobber列表中,但也没有帮助。我只是添加了计时器的代码。这可能会给我们提供更多的线索。@R。。请你确认我对你答案的理解是否正确?你是说如果没有内存,_volatile__;会阻止GCC优化asm,但它仍然可能会根据不相关的指令重新排序?内存阻止asm重新排序。对吗?我没有想到明显的障碍。还请注意,由于指令障碍,CPU将不得不排空管道,从而导致性能损失。这是一个Heisenticker…是的,这几乎是不可能避免的。是的,在线组装说明是用asm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuvolatile\uuuuuuuuuuu编写的。根据你的建议,我将内存添加到了clobber列表中,但也没有帮助。我只是添加了计时器的代码。这可能会给我们提供更多的线索。@R。。请你确认我对你答案的理解是否正确?你是说如果没有内存,_volatile__;会阻止GCC优化asm,但它仍然可能会根据不相关的指令重新排序?内存阻止asm重新排序。这是正确的吗?请务必注意,您的解决方案测量不正确,因为仅发出rdtsc不会阻止CPU在执行时对其重新排序。在rdtsc之前使用lfence或cpuid指令,或者在较新的CPU上使用rdtscp。查看维基百科,它正确地描述了它。@Tothpu:你完全正确。如果担心重新排序,则需要cpuid。如果重新排序不是一个问题,例如由于中间的函数调用,cpuid可以省略。=将在64位代码中错误编译。您将只获得64位时间戳的下半部分。如果要减去两个时间戳,并且间隔足够小,比如对于4.3GHz的CPU来说,大约一秒钟,那么这很好。有关正确的代码,请参阅。需要注意的是,您的解决方案测量不正确,因为仅发出rdtsc不会阻止CPU在执行时对其重新排序。在rdtsc之前使用lfence或cpuid指令,或者在较新的CPU上使用rdtscp。查看维基百科,它正确地描述了它。@Tothpu:你完全正确。如果担心重新排序,则需要cpuid。如果重新排序不是一个问题,例如由于中间的函数调用,cpuid可以省略。=将在64位代码中错误编译。您将只获得64位时间戳的下半部分。如果要减去两个时间戳,并且间隔足够小,比如对于4.3GHz的CPU来说,大约一秒钟,那么这很好。有关正确的代码,请参阅。