我在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核心与OS隔离,或者没有。这意味着测试应用程序可以在独占的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);
}
有人可以分享一些想法吗,那里可能出现什么问题?
答案 0 :(得分:3)
让我们看一下clock_gettime的源代码:
/* Code size doesn't matter (vdso is 4k anyway) and this is faster. */
notrace static int __always_inline do_realtime(struct timespec *ts)
{
unsigned long seq;
u64 ns;
int mode;
do {
seq = gtod_read_begin(gtod);
mode = gtod->vclock_mode;
ts->tv_sec = gtod->wall_time_sec;
ns = gtod->wall_time_snsec;
ns += vgetsns(&mode);
ns >>= gtod->shift;
} while (unlikely(gtod_read_retry(gtod, seq)));
ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
return mode;
}
我们在这里看到的是代码在循环内运行。此循环使用unlikely
条件进行注释。该条件与此代码读取有时更新的共享内存这一事实有关,并且在更新时,代码需要等待更新完成。
对于您的问题,最可能的答案是,在相应的内核代码更新其结构时,您经常会捕获clock_gettime
。当发生这种情况时,代码运行速度明显变慢。
答案 1 :(得分:0)
我认为不是clock_gettime
调用本身的逻辑会花费更长的时间,但不是您的定时循环会被周期性地中断,而是出现了额外的时间作为一个较长的间隔。
也就是说,任何类型的定时循环都容易受到外部事件(例如中断)的干扰。例如,除了具有非常特定的无滴答内核配置(不是默认配置)之外,您的应用程序将被时钟中断周期性地中断,这将进行一些处理以查看是否应运行另一个进程。即使最终没有其他进程最终运行,这也很容易占到几微秒。
此外,硬件可能由于各种原因而暂时暂停,例如frequency transitions,这是在其他内核进入或退出空闲状态时发生的。我在大约8微秒内测量了这些转变,接近您报告的值。在这些暂停期间,CPU不在执行指令,但是TSC一直在运行,因此它以超长的间隔显示。
除此之外,还有一个ton of reasons,为什么您会遇到异常的时间安排。该答案还包括一些方法,您可以根据自己的喜好来缩小可能的原因。
最后,答案建议clock_gettime
本身可能正在阻塞,而内核会更新数据结构。虽然肯定有可能,但我认为它的可能性比其他原因要小。您可以复制并粘贴VDSO代码,然后对其进行修改以记录是否确实发生了阻塞,然后调用该代码以查看您的暂停是否与阻塞相关。我猜不会。