这个想法不是不合理,但它不适合用户模式应用程序,正如@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 保持同步。