假设我的CPU中的所有核心都有相同的频率,从技术上讲,我可以每毫秒左右同步每个核心的系统时间和时间戳计数器对。然后,基于我正在运行的当前内核,我可以获取当前的rdtsc
值,并使用滴答增量除以内核频率,我能够估计自上次同步系统时间和时间戳计数器对以来经过的时间,并推断出当前系统时间,而不需要从当前线程进行系统调用的开销(假设检索上述数据不需要任何锁)。这在理论上很有效,但在实践中我发现有时我得到的节拍比我预期的要多,也就是说,如果我的核心频率是1 1GHz,并且我在1毫秒前获取了系统时间和时间戳计数器对,我希望在节拍中看到大约10^6个节拍的增量,但实际上我发现它可能在10^6到10^7之间。我不确定是什么问题,有人可以分享他对如何使用rdtsc
计算系统时间的想法吗?我的主要目标是避免每次我想知道系统时间时都需要执行系统调用,并且能够在用户空间中执行计算,这将给我的系统时间一个好的估计(目前我定义了一个好的估计结果,与实际系统时间的10微秒间隔。
发布于 2017-02-13 00:22:16
不要直接使用RDTSC
机器指令来-using自己(因为您的操作系统调度程序可能会在任意时刻重新调度其他线程或进程,或者减慢时钟)。使用您的库或操作系统提供的函数。
我的主要目标是避免每次想知道系统时间时都需要执行系统调用
在Linux上,阅读time(7),然后使用clock_gettime(2),它非常快(并且不涉及任何缓慢的system call),这要归功于vdso(7)。
在符合C++11的实现上,只需使用标准 header。标准C具有clock(3) (提供微秒精度)。两者都会在Linux上使用足够好的时间度量函数(因此间接使用vdso
)
上次我测量clock_gettime
时,每次调用花费的时间通常不到4纳秒。
发布于 2017-02-13 01:27:43
这个想法并不是不合理,但它不适合用户模式应用程序,正如@Basile所建议的那样,对于用户模式应用程序,有更好的替代方案。
英特尔自己建议将TSC用作挂钟:
不变的TSC将在所有ACPI,C-中以恒定的速率运行。和T形状态。
这是不断向前发展的架构行为。在具有恒定TSC支持的处理器上,操作系统可以将TSC用于挂钟定时器服务(而不是ACPI或HPET定时器)。TSC读取要高效得多,并且不会产生与环转换或访问平台资源相关的开销。
然而,必须小心。
TSC并不总是不变的
在较老的处理器中,TSC在每个内部时钟周期内递增,它不是挂钟。
引述英特尔
用于奔腾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节。
当然,由于您是从TSC_Value
开始的,因此必须解决ART_Value
问题。你可以忽略K,因为你只对增量感兴趣。从ART_Value
增量,你可以得到经过的时间,一旦你知道了ART的频率。这以k*B的形式给出,其中k是MSR MSR_PLATFORM_INFO
中的常量,B是100 Mhz或133+1/3 Mhz,具体取决于处理器。
正如@BeeOnRope指出的那样,天湖的艺术水晶频率不再是总线频率。
由英特尔维护的实际值可以在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保持时间。
https://stackoverflow.com/questions/42189976
复制相似问题