Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何使用liburing实现每秒零纳秒的计时器?_C_Linux - Fatal编程技术网

C 如何使用liburing实现每秒零纳秒的计时器?

C 如何使用liburing实现每秒零纳秒的计时器?,c,linux,C,Linux,我注意到内核端的io使用的是CLOCK_MONOTONIC at,因此对于第一个计时器,我使用CLOCK_REALTIME和CLOCK_MONOTONIC获取时间,并调整纳秒,如下所示,并使用IORING_TIMEOUT_ABS标志表示io_prep_TIMEOUT 你能告诉我一个比这更好的方法吗 谢谢你的评论!我想更新日志记录的当前时间,如ngx\u time\u update()。我修改了我的示例,只使用了CLOCK\u REALTIME,但仍然延迟了大约400微秒。github.com/h

我注意到内核端的io使用的是CLOCK_MONOTONIC at,因此对于第一个计时器,我使用CLOCK_REALTIME和CLOCK_MONOTONIC获取时间,并调整纳秒,如下所示,并使用IORING_TIMEOUT_ABS标志表示io_prep_TIMEOUT

你能告诉我一个比这更好的方法吗


谢谢你的评论!我想更新日志记录的当前时间,如
ngx\u time\u update()
。我修改了我的示例,只使用了
CLOCK\u REALTIME
,但仍然延迟了大约400微秒。github.com/hnakamur/iorn/commit/…这是否意味着在我的机器上,
clock\u gettime
大约需要400纳秒

是的,听起来有点对。但是,如果您在linux下的
x86
PC上,400 ns的
clock\u gettime
开销可能有点高(数量级更高,请参见下文)。如果您使用的是
arm
CPU(例如Raspberry Pi,
nvidia
Jetson),可能还可以

我不知道你是怎么得到400微秒的。但是,我不得不在linux下做很多实时工作,400 us与我所测量的类似,即在系统调用挂起进程/线程后进行上下文切换和/或唤醒进程/线程的开销

我不再使用
gettimeofday
。我现在只使用了
clock\u gettime(clock\u REALTIME,…)
,因为它是一样的,只是你得到的是纳秒而不是微秒

正如您所知,虽然
clock\u gettime
是一个系统调用,但现在在大多数系统上,它使用
VDSO
层。内核将特殊代码注入到用户空间应用程序中,以便它能够直接访问时间,而无需
系统调用的开销

如果您感兴趣,您可以在
gdb
下运行并反汇编代码,查看它只访问一些特殊的内存位置,而不是执行系统调用

我觉得你不必太担心这个。只需使用
clock\u gettime(clock\u MONOTONIC,…)
并将
标志设置为0。由于
iorn
层正在使用它,因此在调用
ioring
时,开销并没有考虑到这一点

当我做这种事情时,我想/需要计算
clock\u gettime
本身的开销,我在循环中调用
clock\u gettime
(例如1000次),并尝试将总时间保持在[可能的]时间片以下。我在每次迭代中使用时间之间的最小差异。这补偿了任何[可能的]时间损失

最小值是呼叫本身的开销[平均值]

您还可以使用其他技巧来最小化用户空间中的延迟(例如,提高进程优先级、限制CPU相关性和I/O中断相关性),但这些技巧可能涉及更多的事情,如果您不十分小心,它们可能会产生更糟糕的结果

在您开始采取非常措施之前,您应该有一个可靠的方法来测量计时/基准测试,以证明您的结果不能满足计时/吞吐量/延迟要求。否则,你在做复杂的事情,没有真正的/可测量的/必要的好处


下面是我刚刚创建的一些代码,经过简化,但基于我已经拥有/使用的代码来校准开销:

#include <stdio.h>
#include <time.h>

#define ITERMAX     10000

typedef long long tsc_t;

// tscget -- get time in nanoseconds
static inline tsc_t
tscget(void)
{
    struct timespec ts;
    tsc_t tsc;

    clock_gettime(CLOCK_MONOTONIC,&ts);

    tsc = ts.tv_sec;
    tsc *= 1000000000;
    tsc += ts.tv_nsec;

    return tsc;
}

// tscsec -- convert nanoseconds to fractional seconds
double
tscsec(tsc_t tsc)
{
    double sec;

    sec = tsc;
    sec /= 1e9;

    return sec;
}

tsc_t
calibrate(void)
{
    tsc_t tscbeg;
    tsc_t tscold;
    tsc_t tscnow;
    tsc_t tscdif;
    tsc_t tscmin;
    int iter;

    tscmin = 1LL << 62;
    tscbeg = tscget();
    tscold = tscbeg;

    for (iter = ITERMAX;  iter > 0;  --iter) {
        tscnow = tscget();

        tscdif = tscnow - tscold;
        if (tscdif < tscmin)
            tscmin = tscdif;

        tscold = tscnow;
    }

    tscdif = tscnow - tscbeg;

    printf("MIN:%.9f TOT:%.9f AVG:%.9f\n",
        tscsec(tscmin),tscsec(tscdif),tscsec(tscnow - tscbeg) / ITERMAX);

    return tscmin;
}

int
main(void)
{

    calibrate();

    return 0;
}
所以,我的开销是25纳秒[而不是400纳秒]。但是,同样,每个系统在某种程度上都可能有所不同


更新:

请注意,
x86
处理器具有“速度步长”。操作系统可以半自动地向上或向下调整CPU频率。较低的速度可以节省电力。速度越高,性能越好

这是通过启发式方法完成的(例如,如果操作系统检测到进程是一个CPU占用量大的用户,它将加快速度)

为了实现最高速度,linux有以下目录:

/sys/devices/system/cpu/cpuN/cpufreq
其中
N
是cpu编号(例如0-7)

在这个目录下,有许多感兴趣的文件。它们应该是不言自明的

特别是,请查看
缩放\u调节器
。它具有
ondemand
[内核将根据需要进行调整]或
性能
[内核将强制实现最大CPU速度]

要强制最大速度,作为根,将此[一次]设置为性能(例如):

对所有CPU执行此操作

然而,我只是在我的系统上这样做,效果很小。因此,内核的启发式可能有所改进


至于400us,当一个进程一直在等待某件事情,当它被“唤醒”时,这是一个两步的过程

进程被标记为“可运行”

在某个时刻,系统/CPU会重新调度。进程将根据调度策略和有效的进程优先级运行

对于许多系统调用,重新调度[仅]发生在下一个系统计时器/时钟滴答声/中断时。因此,对于某些情况,如果
HZ
值为1000,则可能会有高达一个完整时钟滴答声(即)的延迟,这可能会在1毫秒(1000 us)之后

平均而言,这是
HZ
或500 us的一半

对于某些系统调用,当进程标记为runnable时,会立即执行重新调度。如果进程具有更高的优先级,它将立即运行

当我第一次看到这个[circa 2004]时,我查看了内核中的所有代码路径,唯一一个立即重新调度的系统调用是SysV IPC,用于
msgsnd/msgrcv
。也就是说,当进程A执行msgsnd时,等待给定消息的任何进程B都将运行

但是,其他人没有(例如,futex)。他们会等待计时器的滴答声。从那时起,情况发生了很大变化,现在,更多的系统调用将立即进行重新调度。例如,我最近测量了
futex
[通过
pthread\u mutex\u*
]调用],它似乎可以快速重新调度

此外,内核调度程序也发生了更改。较新的调度程序可以在很短的时间内唤醒/运行某些事情

那么,对你来说,400
#include <stdio.h>
#include <time.h>

#define ITERMAX     10000

typedef long long tsc_t;

// tscget -- get time in nanoseconds
static inline tsc_t
tscget(void)
{
    struct timespec ts;
    tsc_t tsc;

    clock_gettime(CLOCK_MONOTONIC,&ts);

    tsc = ts.tv_sec;
    tsc *= 1000000000;
    tsc += ts.tv_nsec;

    return tsc;
}

// tscsec -- convert nanoseconds to fractional seconds
double
tscsec(tsc_t tsc)
{
    double sec;

    sec = tsc;
    sec /= 1e9;

    return sec;
}

tsc_t
calibrate(void)
{
    tsc_t tscbeg;
    tsc_t tscold;
    tsc_t tscnow;
    tsc_t tscdif;
    tsc_t tscmin;
    int iter;

    tscmin = 1LL << 62;
    tscbeg = tscget();
    tscold = tscbeg;

    for (iter = ITERMAX;  iter > 0;  --iter) {
        tscnow = tscget();

        tscdif = tscnow - tscold;
        if (tscdif < tscmin)
            tscmin = tscdif;

        tscold = tscnow;
    }

    tscdif = tscnow - tscbeg;

    printf("MIN:%.9f TOT:%.9f AVG:%.9f\n",
        tscsec(tscmin),tscsec(tscdif),tscsec(tscnow - tscbeg) / ITERMAX);

    return tscmin;
}

int
main(void)
{

    calibrate();

    return 0;
}
MIN:0.000000019 TOT:0.000254999 AVG:0.000000025
/sys/devices/system/cpu/cpuN/cpufreq
echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor