即使使用 VDSO,clock_gettime 也可能很慢

Kon*_*kin 3 linux time-measurement vdso

我在 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)

任何人都可以分享一些想法,那里可能有什么问题?

Bee*_*ope 8

我不认为调用clock_gettime本身逻辑周期性地花费更长的时间,而是您的定时循环周期性地被中断,并且这个额外的时间显示为超长的间隔。

也就是说,任何类型的定时循环都会被外部事件(例如中断)中断。例如,除非使用非常特定的无滴答内核配置(不是默认配置),否则您的应用程序将被调度程序中断定期中断,调度程序中断将进行一些处理以查看是否应该运行另一个进程。即使最终没有其他进程运行,这也很容易占用几微秒。

此外,硬件可能会因各种原因而暂时暂停,例如当其他内核进入或离开空闲状态时发生的频率转换。我在大约 8 微秒内测量了这些转换,接近您报告的值。在这些暂停期间,CPU 不执行指令,但TSC继续运行,因此它显示为超长间隔。

除此之外,还有很多原因会导致您遇到异常时间。该答案还包括如果您感兴趣的话可以缩小可能原因范围的方法。

最后,答案表明clock_gettime本身可能是阻塞的,而内核更新数据结构。虽然这当然有可能,但我认为它的可能性比其他原因要小。您可以复制并粘贴 VDSO 代码,然后修改它以记录是否实际发生了任何阻塞,并调用它来查看您的暂停是否与阻塞相关。我猜不会。


Sha*_*esh 5

让我们看一下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;
}
Run Code Online (Sandbox Code Playgroud)

我们在这里看到的是代码在循环中运行。这个循环用unlikely条件注释。该条件与以下事实有关:此代码读取有时会更新的共享内存,并且在更新时,代码需要等待更新完成。

那么,对您的问题最有可能的答案是,clock_gettime当相应的内核代码更新其结构时,您经常会发现。发生这种情况时,代码运行速度明显变慢。