【问题标题】:clock_gettime might be very slow even using VDSO即使使用 VDSO,clock_gettime 也可能非常慢
【发布时间】:2018-02-02 11:03:46
【问题描述】:

我在 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 库,可以解释。但是,每个clock_gettime都使用VDSO(我通过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);
}  

谁能分享一些想法,那里可能有什么问题?

【问题讨论】:

    标签: linux time-measurement vdso


    【解决方案1】:

    我们来看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。发生这种情况时,代码运行速度会明显变慢。

    【讨论】:

    • 看来,我们不仅会在这样的综合测试中遇到“内核正在更新某些东西”的等待。如果内核只需要几微秒来更新一些内存,那就太奇怪了……
    【解决方案2】:

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

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

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

    除此之外,还有一个ton of reasons 为什么您会遇到异常时间。该答案还包括您可以缩小可能的原因(如果您感兴趣)的方法。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-21
      • 1970-01-01
      • 2012-07-27
      • 2013-04-20
      相关资源
      最近更新 更多