【问题标题】:Precise Linux Timing - What Determines the Resolution of clock_gettime()?精确的 Linux 计时 - 什么决定了 clock_gettime() 的分辨率?
【发布时间】:2013-08-22 23:37:47
【问题描述】:

我需要精确计时到 1 us 级别,以计时 pwm 波占空比的变化。

背景

根据 /proc/cpuinfo,我正在使用 Gumstix Over Water COM (https://www.gumstix.com/store/app.php/products/265/),它有一个以 499.92 BogoMIPS 运行的单核 ARM Cortex-A8 处理器(Gumstix 页面声称最高 1Ghz,建议使用 800Mhz)。该操作系统是基于内核版本 2.6.34 的 Angstrom Image 版本的 Linux,它在 Gumstix Water COM 上有现货。

问题

我已经阅读了大量关于 Linux 中精确计时的文章(并且已经尝试了大部分),并且一致认为使用 clock_gettime() 并引用 CLOCK_MONOTONIC 是最好的方法。 (我本来希望使用 RDTSC 寄存器进行计时,因为我有一个具有最低节能能力的内核,但这不是英特尔处理器。)所以这是奇怪的部分,而 clock_getres() 返回 1,表明分辨率为 1 ns ,实际的时序测试表明最小分辨率为 30517ns 或(不可能是巧合)恰好是 32.768KHz 时钟滴答之间的时间。这就是我的意思:

// Stackoverflow example
#include <stdio.h>
#include <time.h>    

#define SEC2NANOSEC 1000000000

int main( int argc, const char* argv[] )
{               
    // //////////////// Min resolution test //////////////////////
    struct timespec resStart, resEnd, ts;
    ts.tv_sec  = 0; // s
    ts.tv_nsec = 1; // ns
    int iters = 100;
    double resTime,sum = 0;    
    int i;
    for (i = 0; i<iters; i++)
    {
        clock_gettime(CLOCK_MONOTONIC, &resStart);      // start timer
        // clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
        clock_gettime(CLOCK_MONOTONIC, &resEnd);        // end timer
        resTime = ((double)resEnd.tv_sec*SEC2NANOSEC + (double)resEnd.tv_nsec 
                  - ((double)resStart.tv_sec*SEC2NANOSEC + (double)resStart.tv_nsec);
        sum = sum + resTime;
        printf("resTime = %f\n",resTime);
    }    
    printf("Average = %f\n",sum/(double)iters);
}

(不要为双重投射而烦恼,tv_sec 在 time_t 中,tv_nsec 很长。)

编译:

gcc soExample.c -o runSOExample -lrt

运行:

./runSOExample

如图所示注释掉 nanosleep 后,结果为 0ns 或 30517ns,大多数为 0ns。这让我相信 CLOCK_MONOTONIC 以 32.768kHz 更新,并且大部分时间在第二次 clock_gettime() 调用之前时钟尚未更新,如果结果为 30517ns,则时钟已在两次调用之间更新。

当我在我的开发计算机(运行在 1.4 GHz 的 AMD FX(tm)-6100 六核处理器)上做同样的事情时,最小延迟是更恒定的 149-151ns,没有零。

所以,让我们将这些结果与 CPU 速度进行比较。对于 Gumstix,30517ns (32.768kHz) 相当于 499.93MHz cpu 的 15298 个周期。对于我的开发计算机,150ns 相当于 1.4Ghz CPU 的 210 个周期。

使用未注释的 clock_nanosleep() 调用,平均结果如下: Gumstix:平均值 = 213623,结果上下变化,以最小分辨率 30517ns 的倍数变化 开发计算机:57710-68065 ns,没有明显趋势。在开发计算机的情况下,我希望分辨率实际上处于 1 ns 水平,而测量到的 ~150ns 确实是两个 clock_gettime() 调用之间经过的时间。

所以,我的问题是: 是什么决定了最小分辨率? 当处理器的运行速度仅快 2.6 倍时,为什么开发计算机的分辨率比 Gumstix 高 30000 倍? 有没有办法改变 CLOCK_MONOTONIC 的更新频率和位置?在内核中?

谢谢!如果您需要更多信息或说明,请询问。

【问题讨论】:

  • 只是想知道。不需要某种类型的硬件来从 CPU 时钟中获取周期吗?至少在那个精度水平上。也许Gumstix没有那个? (我只是在谈论一个可以计算滴答声并在滚动之前保持它们的寄存器。)
  • 根据您所说的所有阅读内容,您可能已经看过 this link,但还是发帖以防万一。有些材料是相关的。
  • 有完全相同的问题。 30517ns 的倍数。我正在使用 Overo Tide。你解决过这个问题吗?

标签: c precision timing gettime gumstix


【解决方案1】:

据我了解,两种环境(Gumstix 和您的开发计算机)之间的区别可能是它们使用的底层计时器硬件。

评论 nanosleep() 案例:

您正在使用clock_gettime() 两次。为了让您大致了解这个 clock_gettime() 最终将映射到什么(在内核中):

clock_gettime -->clock_get() -->posix_ktime_get_ts -->ktime_get_ts() -->timekeeping_get_ns() -->clock->read()

clock->read() 基本上读取底层定时器驱动程序和相应硬件提供的计数器的值。与过去的计数器存储值和当前计数器值的简单区别,然后是纳秒转换数学将得出经过的纳秒,并将更新内核中的计时数据结构。

例如,如果您有一个 HPET 计时器,它为您提供 10 MHz 时钟,则硬件计数器将以 100 ns 的时间间隔更新。

比方说,在第一个时钟->read() 时,您会得到一个计数器值 X。

Linux 计时数据结构将读取 X 的这个值,得到与一些旧的存储计数器值相比的差异 'D'。做一些计数器差异 'D' 到纳秒 'n' 的转换数学,更新数据- 'n' 的结构 为用户空间提供这个新的时间值。

当发出第二个时钟->read() 时,它会再次读取计数器并更新时间。 现在,对于 HPET 计时器,此计数器每 100ns 更新一次,因此,您将看到此差异报告给用户空间。

现在,让我们用慢速 32.768 KHz 时钟替换这个 HPET 计时器。现在,clock->read() 的计数器将仅在 30517 ns 秒后更新,因此,如果您在此期间之前第二次调用 clock_gettime(),您将得到 0(大多数情况下)并且在某些情况下,您的第二个函数调用将在计数器增加 1 后进行,即 30517 ns 已过。因此,有时值为 30517 ns。

未注释的 Nanosleep() 案例: 让我们跟踪单调时钟的clock_nanosleep():

clock_nanosleep() -->nsleep --> common_nsleep() -->hrtimer_nanosleep() -->do_nanosleep()

do_nanosleep() 将简单地将当前任务置于 INTERRUPTIBLE 状态,将等待计时器到期(即 1 ns)然后再次将当前任务设置为 RUNNING 状态。你看,现在涉及到很多因素,主要是当你的内核线程(以及用户空间进程)将再次被调度时。根据您的操作系统,您在执行上下文切换时总是会遇到一些延迟,这就是我们观察到的平均值。

现在你的问题:

什么决定了最小分辨率?

我认为您系统的分辨率/精度将取决于所使用的底层计时器硬件(假设您的操作系统能够为用户空间进程提供该精度)。

*为什么当处理器运行速度仅快 2.6 倍时,开发计算机的分辨率比 Gumstix 高 30000 倍?*

对不起,我在这里想你了。 它是如何快 30000 倍的? 对我来说,它看起来快了 200 倍(30714 ns/150 ns ~ 200X ?)。但无论如何,据我所知,CPU 速度可能与也可能无关计时器分辨率/精度。因此,这种假设在某些架构中可能是正确的(当您使用 TSC H/W 时),但在其他架构中可能会失败(使用 HPET、PIT 等)。

有没有办法改变 CLOCK_MONOTONIC 的更新频率和位置?在内核中?

您可以随时查看内核代码以获取详细信息(这就是我查看它的方式)。 在 linux 内核代码中,查找这些源文件和文档:

  1. 内核/posix-timers.c
  2. 内核/hrtimer.c
  3. 文档/timers/hrtimers.txt

【讨论】:

    【解决方案2】:

    我手头没有 gumstix,但您的时钟源似乎很慢。 运行:

    $ dmesg | grep clocksource

    如果你回来

    [ 0.560455] Switching to clocksource 32k_counter

    这或许可以解释为什么您的时钟如此缓慢。

    在最近的内核中有一个目录/sys/devices/system/clocksource/clocksource0,其中包含两个文件:available_clocksourcecurrent_clocksource。如果您有此目录,请尝试通过将其名称回显到第二个文件来切换到不同的源。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-13
      相关资源
      最近更新 更多