【问题标题】:Calculate system time using rdtsc使用 rdtsc 计算系统时间
【发布时间】:2017-06-30 14:34:45
【问题描述】:

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

【问题讨论】:

    标签: c++ assembly time rdtsc


    【解决方案1】:

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

    英特尔自己建议将 TSC 用作挂钟:

    不变的 TSC 将在所有 ACPI P-、C- 中以恒定速率运行。和 T 状态。
    这是架构行为 向前进。在具有不变 TSC 支持的处理器上,操作系统可以将 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 没有序列化

    您需要从上到下对其进行序列化。
    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,因为您只对增量感兴趣。 一旦您知道 ART 的频率,您就可以从 ART_Value delta 获取经过的时间。这以 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 保持同步。

    【讨论】:

    • 在 Intel 上,自 Skylake 以来还有一个更复杂的问题:TSC 不是以 BCLK 的倍数(100/133 MHz)运行,而是以时钟晶振的倍数运行,即(对于示例)在 Skylake 上为 24 MHz,但在其他架构上具有不同的频率。我不会填写所有细节,而是查看由英特尔为新处理器版本相当可靠地维护的turbostat source。上面的链接显示了计算 tsc hz 的方法,其中 tsc != M * BCLK。
    • ... 特别是,这意味着 TSC 频率甚至不再等于 标称 CPU 频率,因为对于任何整数,通常 M * 100 != T * 24 M,T。例如,标称频率为 2600 MHz,您的 CPU 乘数为 26,但对于 2592 MHz 的 TSC 频率,您可以获得的最接近 TSC 的乘数是 T == 108。
    • @BeeOnRope 谢谢,该链接很有用!我发现了关于 ART 晶体频率的不同来源(有些提到 24Mhz),但没有一个是确定的。 TSC_value 是 ART_value 的比值,即使 ART_freq 为 24,很容易找到 k1 和 k2 使得 2600 = ART_freq * k1 / k2。我知道 Intel 可以选择以不同的频率运行 TSC,那是什么你的意思是在你的第二条评论?
    • 我对最后一条评论的意思是,即使您可以选择 k1k2 使得 TSC_Value 与标称频率相同,但实际上,它以相反的方式工作:TSC 由硬件实现确定,该硬件实现使用某些基本块的时钟乘法。在 Skylake 中,基本时钟从 BCLK 切换到 24 MHz/25Mhz/... 时钟晶体,倍频器通常是整数。在我的机器上,乘数为 108,即 24 MHz * 108 = 2592 MHz,k1 == 216k2 == 2。标称 CPU 频率为 26 * 100 = 2600 MHz。
    • 所以CPUID.15H 正是英特尔表示某些基本时钟和完整 TSC 频率之间的实际关系的方式,但硬件实现不如表示。可能使用k1 / k2 比率而不是简单的整数乘法器来反映您经常进行时钟乘法(由k1 处理)的事实,有时您也有时钟分频(例如,某些计数器每1024 只计数一个)周期或其他东西),但值不是任意的。
    【解决方案2】:

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

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

    在 Linux 上,阅读time(7),然后使用clock_gettime(2),这非常快(并且不涉及任何慢速system call),这要归功于vdso(7)

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

    上次我测量clock_gettime 时,每次调用通常不到 4 纳秒。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-26
    • 2012-10-22
    • 2013-08-14
    • 2019-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多