我正在使用以下代码来分析我的操作,以优化函数中使用的cpu周期。

static __inline__ unsigned long GetCC(void)
{
  unsigned a, d;
  asm volatile("rdtsc" : "=a" (a), "=d" (d));
  return ((unsigned long)a) | (((unsigned long)d) << 32);
}

我不认为这是最好的,因为即使连续两次通话也给我带来“33”的不同。
有什么建议么 ?

最佳答案

我个人认为rdtsc指令很棒并且可以用于各种任务。我认为使用cpuid不必为rdtsc做准备。这是我围绕rdtsc进行推理的方式:

  • 因为我使用Watcom编译器,所以我已经使用“#pragma aux”实现了rdtsc,这意味着C编译器将生成内联指令,请在edx:eax中期待结果,并告知其优化器eax和edx的内容已包含被修改。与传统的_asm实现相比,这是一个巨大的改进,在传统的_asm实现中,优化器将避免在_asm附近进行优化。我还使用“#pragma aux”实现了divide_U8_by_U4,以便在将clock_cycles转换为我们或ms时不需要调用lib函数。
  • 每次执行rdtsc都将导致一些开销(如果按照作者的示例进行封装,则会增加很多),而要测量的序列越短,则必须考虑更多的开销。通常,我不会为比内部时钟频率的1/30更短的时间计时,该频率通常为1/10 ^ 8秒(3 GHZ内部时钟)。我将这些测量值用作指示,而不是事实。知道这一点,我可以省去cpuid。我测量的次数越多,就越接近事实。
  • 为了可靠地进行测量,我将使用1/100-1/300范围,即0.03-0.1 us。在此范围内,使用cpuid的附加精度实际上是微不足道的。我将此范围用于较短的时序计时。这是我的“非标准”单元,因为它取决于CPU的内部时钟频率。例如,在1 GHz的计算机上,我不会使用0.03 us,因为这会使我超出1/100的限制,并且我的读数将成为指示。在这里,我将使用0.1 us作为最短时间测量单位。 1/300不会被使用,因为它太接近1 us(请参阅下文),不会产生任何显着差异。
  • 对于更长的处理序列,我将两个rdtsc读数之间的差除以3000(对于3 GHz),并将经过的时钟周期转换为我们。实际上,我使用(diff + 1500)/3000,其中1500是3000的一半。对于I/O等待,我使用毫秒=>(diff + 1500000)/3000000。这些是我的“标准”单位。我很少使用秒。
  • 有时我得到的结果出乎意料的缓慢,然后我必须问自己:这是由于中断还是由于代码?我再测量几次,看看是否确实是一个中断。在这种情况下,在现实世界中,井间断断续续地发生着。如果我的序列很短,则很有可能不会中断下一次测量。如果序列较长,则中断会更频繁地发生,而我对此无能为力。
  • 非常准确地测量较长的耗时(一个小时或更长的ET或更低的时间)将增加在divid_U8_by_U4中获得除法异常的风险,因此,我考虑了何时使用我们以及何时使用ms。
  • 我也有基本统计信息的代码。使用此工具,我可以记录最小值和最大值,并可以计算平均值和标准偏差。该代码是不平凡的,因此必须从测得的ET中减去其自身的ET。
  • 如果编译器正在进行广泛的优化,并且您的读数存储在局部变量中,则编译器可以确定(“正确”)可以省略代码。避免这种情况的一种方法是将结果存储在公共(public)(非静态,基于非堆栈的)变量中。
  • 在现实条件下运行的程序应该在现实条件下进行测量,没有办法解决。

  • 关于时间戳计数器是否准确的问题,我要说的是,假设不同内核上的tsc是同步的(这是正常的),那么在事件减少期间就存在CPU节流的问题,以减少能耗。测试时始终可以禁止该功能。如果您在同一处理器上以1 GHz或10 Mhz的速度执行一条指令,则经过的周期数将是相同的,即使前者在1%的时间内完成了压缩的时间。

    10-07 19:02