在最近的CPU上(至少在过去十年左右),除了各种可配置的性能计数器之外,英特尔还提供了三个固定功能硬件性能计数器.三个固定柜台是:
INST_RETIRED.ANY
CPU_CLK_UNHALTED.THREAD
CPU_CLK_UNHALTED.REF_TSC
Run Code Online (Sandbox Code Playgroud)
第一个计算退役指令,第二个计算实际周期,最后一个是我们感兴趣的."英特尔软件开发人员手册"第3卷的描述如下:
当核心未处于暂停状态而不处于TM停止时钟状态时,此事件计算TSC速率下的参考周期数.核心在运行HLT指令或MWAIT指令时进入暂停状态.此事件不受核心频率变化(例如,P状态)的影响,但计数与时间戳计数器的频率相同.当核心未处于暂停状态而不处于TM stopclock状态时,此事件可以估计经过的时间.
因此,对于CPU绑定循环,我希望该值与从中读取的自由运行TSC值相同rdstc,因为它们应该仅针对暂停的循环指令或"TM stopclock state"是什么发散.
我使用以下循环测试它(整个独立演示在github上可用):
for (int i = 0; i < 100; i++) {
PFC_CNT cnt[7] = {};
int64_t start = nanos();
PFCSTART(cnt);
int64_t tsc =__rdtsc();
busy_loop(CALIBRATION_LOOPS);
PFCEND(cnt);
int64_t tsc_delta = __rdtsc() - tsc;
int64_t nanos_delta = nanos() - start;
printf(CPU_W "d" REF_W ".2f" TSC_W ".2f" MHZ_W ".2f" RAT_W ".6f\n",
sched_getcpu(),
1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / nanos_delta,
1000.0 * tsc_delta / nanos_delta,
1000.0 * CALIBRATION_LOOPS / nanos_delta,
1.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/tsc_delta); …Run Code Online (Sandbox Code Playgroud) 我想要clock_gettime(CLOCK_REALTIME,...)花多长时间打电话."回到当天"我曾经在循环的顶部称它为一次,因为它是一个相当昂贵的电话.但是现在,我希望通过vDSO和一些时钟改进,它可能不会那么慢.
我写了一些测试代码,用于__rdtscp计时重复调用clock_gettime(rdtscp调用绕过一个调用clock_gettime并将结果添加到一起的循环,这样编译器就不会进行太多的优化).
如果我clock_gettime()快速连续呼叫,时间长度从大约45k时钟周期下降到500个周期.其中一些我认为可能是第一次调用必须加载vDSO代码(仍然没有完全对我有意义),但如何需要一些调用来获得500我根本无法解释,这种行为似乎无论我如何测试它都是恒定的:
42467
1114
1077
496
455
Run Code Online (Sandbox Code Playgroud)
但是,如果我在调用clock_gettime之间休眠(一秒或十分,无关紧要),它只会达到约4.7k周期的稳定状态:
这里睡10秒钟:
28293
1093
4729
4756
4736
Run Code Online (Sandbox Code Playgroud)
这里睡1秒钟:
61578
855
4753
4741
5645
4753
4732
Run Code Online (Sandbox Code Playgroud)
缓存行为似乎无法描述这一点(在桌面系统上没有做太多任何事情).我应该为clock_gettime的调用预算多少钱?为什么呼叫变得越来越快?为什么睡一小段时间这么重要?
tl; dr我试图理解调用clock_gettime(CLOCK_REALTIME,...)所花费的时间不理解为什么它在快速连续调用时运行得更快而不是在调用之间调用.
更新:这是proc 0上的cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 158
model name : Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
stepping : 9
microcode : 0x84
cpu MHz : 2800.000
cache size : 6144 KB …Run Code Online (Sandbox Code Playgroud) 我在 Intel(R) Xeon(R) CPU E5-2667 v4 @ 3.20GHz 上使用 CentOS Linux 7.3.1611 版
在我的用户空间应用程序测试期间,我注意到 clock_gettime(CLOCK_MONOTONIC, &ts) 可能需要 5-6 微秒而不是平均约 23 纳秒。它可能每 10000 次后续调用只发生一次,但是它可能会发生。
如果没有 VDSO 库,则可以解释。但是,VDSO 用于每个clock_gettime(我通过strace 检查过)。
无论相应的线程是否关联到某个 CPU 内核。不管这个CPU内核是否与操作系统隔离。这意味着测试应用程序可能会在独占 CPU 内核上运行,而无论如何可能会出现延迟!
我通过比较两个随后的 clock_gettime 调用的结果来测量延迟,例如:
unsigned long long __gettimeLatencyNs() {
struct timespec t1_ts;
struct timespec t2_ts;
clock_gettime(CLOCK_MONOTONIC, &t1_ts);
clock_gettime(CLOCK_MONOTONIC, &t2_ts);
return ((t2_ts.tv_sec - t1_ts.tv_sec)*NANO_SECONDS_IN_SEC + t2_ts.tv_nsec - t1_ts.tv_nsec);
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以分享一些想法,那里可能有什么问题?
linux ×2
performance ×2
benchmarking ×1
optimization ×1
rdtsc ×1
timing ×1
vdso ×1
x86 ×1
x86-64 ×1