使用rdtsc计算系统时间

e27*_*314 4 c++ time assembly rdtsc

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

Bas*_*tch 7

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

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

在Linux上,读取时间(7)然后使用clock_gettime(2),这非常快(并且不涉及任何慢速系统调用),这要归功于vdso(7).

在符合C++ 11的实现上,只需使用标准<chrono>.标准C具有时钟(3)(给出微秒精度).两者都会在Linux上使用足够好的时间测量功能(间接地 vdso)

上次我测量clock_gettime它通常每次通话花费不到4纳秒.


Mar*_*oom 6

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

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

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

但是,必须小心.

TSC并不总是不变的

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

对于Pentium M处理器(系列[06H],型号[09H,0DH]); 适用于Pentium 4处理器,Intel Xeon处理器(系列[0FH],型号[00H,01H或02H]); 对于P6系列处理器:时间戳计数器随每个内部处理器时钟周期递增.

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

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

TSC不会以品牌字符串上建议的频率递增

还在引用英特尔

时间戳计数器以恒定速率递增.该速率可以由处理器的最大核心时钟与总线时钟比率来设置,或者可以由处理器启动的最大分辨率频率来设置.最大分辨频率可能与处理器基频不同.
在某些处理器上,TSC频率可能与品牌字符串中的频率不同.

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

rdtsc 不是序列化

您需要从上方和下方序列化它.
看到这个.

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

正确的公式是

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

请参阅英特尔手册3的第17.15.4节.

当然,你必须解决,ART_Value因为你从一开始TSC_Value.您可以忽略K,因为您只对增量感兴趣.从ART_Value三角洲中,您可以在知道ART的频率后获得经过的时间.这是以k*B给出的,其中k是MSR中的常数MSR_PLATFORM_INFO,B是100Mhz或133 + 1/3Mhz,具体取决于处理器.

正如@BeeOnRope指出的那样,来自Skylake的ART晶振频率不再是总线频率.
可以在turbostat.c文件中找到由Intel维护的实际值.

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; 
}
Run Code Online (Sandbox Code Playgroud)

当处理器进入深度睡眠时,TSC不会递增

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

上下文切换会使测量中毒

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