使用rdtsc

时间:2017-02-12 16:14:19

标签: c++ assembly time rdtsc

假设我的CPU中的所有内核具有相同的频率,从技术上讲,我可以每毫秒左右为每个内核同步系统时间和时间戳计数器对。然后根据我运行的当前核心,我可以获取当前rdtsc值并使用tick delta除以核心频率I,我能够估计自上次同步以来经过的时间系统时间和时间戳计数器对,并推导出当前系统时间,而没有来自当前线程的系统调用开销(假设不需要锁来检索上述数据)。 这在理论上很有效,但在实践中我发现有时我会得到更多的滴答,然后我会期望,也就是说,如果我的核心频率为1GHz并且我花了1毫秒的系统时间和时间戳计数器对,我希望看到一个delta在大约10 ^ 6个刻度的刻度线中,但实际上我发现它可以在10 ^ 6到10 ^ 7之间的任何位置。 我不确定有什么问题,任何人都可以分享他对如何使用rdtsc计算系统时间的看法吗?我的主要目标是避免每次我想知道系统时间时执行系统调用的需要,并且能够在用户空间中执行计算,这将给我一个很好的估计(目前我定义了一个很好的估计结果)与实际系统时间间隔为10微秒。

2 个答案:

答案 0 :(得分:7)

不要这样做 - 直接使用RDTSC机器指令 - (因为您的操作系统调度程序可以在任意时刻重新安排其他线程或进程,或者减慢时钟速度)。使用库或操作系统提供的功能。

  

我的主要目标是每次想知道系统时间时都不需要执行系统调用

在Linux上,请阅读time(7),然后使用clock_gettime(2),这非常快(并且不会涉及任何慢system call),感谢vdso(7)

在符合C ++ 11的实现上,只需使用标准<chrono> header即可。标准C具有clock(3)(精确到微秒)。两者都会在Linux上使用足够好的时间测量功能(所以间接 vdso

上次我测量clock_gettime时,每次通话的时间通常不到4纳秒。

答案 1 :(得分:6)

这个想法并不健全,但它不适合用户模式应用程序,正如@Basile所建议的那样,有更好的选择。

英特尔本身建议将TSC用作挂钟:

  

不变的TSC将在所有ACPI P-,C-中以恒定速率运行。和T状态。
   这是架构行为   向前进。在具有不变TSC支持的处理器上,OS可以将TSC用于挂钟计时器服务   (而不是ACPI或HPET计时器)。 TSC读取效率更高,并且不会产生与之相关的开销   环转换或访问平台资源。

但是,必须小心。

TSC并不总是不变的

在较旧的处理器中,TSC在每个内部时钟周期递增,它不是挂钟。
引用英特尔

  

对于Pentium M处理器(系列[06H],型号[09H,0DH]);适用于奔腾4处理器,英特尔至强处理器   (族[0FH],型号[00H,01H或02H]);对于P6系列处理器:时间戳计数器递增   每个内部处理器时钟周期。

     

内部处理器时钟周期由当前内核时钟与总线时钟比决定。英特尔   SpeedStep®技术转换也可能影响处理器时钟。

如果您只有变量TSC,则测量对于跟踪时间是不可靠的。 但是对于不变的TSC有希望。

TSC未按照品牌字符串

上建议的频率递增

仍在引用英特尔

  

时间戳计数器以恒定速率递增。该比率可以由   处理器的最大内核时钟与总线时钟比,或者可以通过最大分辨频率设置   哪个处理器启动了。最大解析频率可能与处理器基数不同   频率。
  在某些处理器上,TSC频率可能不相同   作为品牌字符串中的频率。

您不能简单地采用处理器盒子上写的频率 见下文。

rdtsc不是序列化

您需要从上方和下方序列化 请参阅this

TSC基于ART(始终运行计时器)时不变

正确的公式是

TSC_Value = (ART_Value * CPUID.15H:EBX[31:0] )/ CPUID.15H:EAX[31:0] + K

参见英特尔手册3的第17.15.4节。

当然,您需要解决ART_Value,因为您从TSC_Value开始。您可以忽略 K ,因为您只对deltas感兴趣。 从ART_Value delta开始,您可以在知道ART的频率后获得时间。这是以 k * B 给出的,其中 k 是MSR MSR_PLATFORM_INFO B 中的常量是100Mhz或133 + 1/3 Mhz,具体取决于处理器。

正如@BeeOnRope指出的那样,来自Skylake的ART晶振频率不再是总线频率 英特尔维护的实际值可以找到in the turbostat.c file

switch(model) 
{
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP:    /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE:    /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP:   /* KBL */
    crystal_hz = 24000000;  /* 24.0 MHz */
    break;
case INTEL_FAM6_SKYLAKE_X:  /* SKX */
case INTEL_FAM6_ATOM_DENVERTON: /* DNV */
    crystal_hz = 25000000;  /* 25.0 MHz */
    break;
case INTEL_FAM6_ATOM_GOLDMONT:  /* BXT */
    crystal_hz = 19200000;  /* 19.2 MHz */
    break;
default:
    crystal_hz = 0; 
}

当处理器进入深度睡眠

时,TSC不会递增

这应该不是单个套接字机器上的问题,但是Linux内核对即使在非深度睡眠状态下重置TSC也有一些评论。

上下文切换会使测量中毒

你无能为力 这实际上阻止了您与TSC的时间保持。