【问题标题】:Scheduling events at microsecond granularity in POSIX在 POSIX 中以微秒粒度调度事件
【发布时间】:2013-11-24 11:40:55
【问题描述】:

我正在尝试确定我可以准确安排在 C/C++ 中发生的任务的粒度。目前我可以可靠地安排任务每 5 微秒发生一次,但我正在尝试看看是否可以进一步降低。

如果可能的话,我们将不胜感激有关如何实现这一目标的任何建议。

因为我知道计时器粒度通常取决于操作系统:我目前在 Linux 上运行,但如果时间粒度更好,我会使用 Windows(虽然我不相信它是基于我发现的查询性能计数器)

我在裸机(无 VM)上执行所有测量。 /proc/timer_info 确认我的 CPU 的纳秒计时器分辨率(但我知道这不会转化为纳秒警报分辨率)

当前

My current code can be found as a Gist here

目前,我能够每 5 微秒(5000 纳秒)执行一次请求,延迟到达率不到 1%。当确实发生延迟到达时,它们通常只落后一个周期(5000 纳秒)。

我现在正在做三件事

将进程设置为实时优先级(@Spudd86 here 指出的一些)

struct sched_param schedparm;
memset(&schedparm, 0, sizeof(schedparm));
schedparm.sched_priority = 99; // highest rt priority
sched_setscheduler(0, SCHED_FIFO, &schedparm);

最大限度地减少计时器的延迟

prctl(PR_SET_TIMERSLACK, 1);

使用 timerfds(2.6 Linux 内核的一部分)

int timerfd = timerfd_create(CLOCK_MONOTONIC,0);
struct itimerspec timspec;
bzero(&timspec, sizeof(timspec));
timspec.it_interval.tv_sec = 0;
timspec.it_interval.tv_nsec = nanosecondInterval;
timspec.it_value.tv_sec = 0;
timspec.it_value.tv_nsec = 1;

timerfd_settime(timerfd, 0, &timspec, 0);

可能的改进

  1. 为此进程指定一个处理器?
  2. 使用非阻塞 timerfd,这样我就可以创建一个紧密循环,而不是阻塞(紧密循环会浪费更多 CPU,但也可以更快地响应警报)
  3. 使用外部嵌入式设备触发(无法想象为什么会更好)

为什么

我目前正在为基准测试引擎创建工作负载生成器。工作负载生成器使用泊松过程模拟到达率(X 个请求/秒等)。从泊松过程中,我可以确定必须从基准测试引擎发出请求的相对时间。

因此,例如,每秒 10 个请求,我们可能会在以下位置发出请求: t = 0.02, 0.04, 0.05, 0.056, 0.09 秒

这些请求需要提前调度,然后执行。随着每秒请求数量的增加,调度这些请求所需的粒度也会增加(每秒数千个请求需要亚毫秒级的精度)。因此,我试图弄清楚如何进一步扩展这个系统。

【问题讨论】:

  • 您是否已在启用 CONFIG_PREEMPT_RT 标志的情况下将内核修补为实时?
  • @Archie 不,我没有——感谢您向我指出这一点。
  • 我认为您的问题与 C 或 C++ 没有任何关系。它们只是您的界面语言,不是吗?所以也许删除标签,并在你的问题标题中用 POSIX 替换 C++。
  • @JensGustedt 我认为 C 会让我在这个领域的人有更多的知名度?
  • @BSchlinker,“可见性”不应该是你的目标。标签和标题是为了吸引具有正确技能的专家来解决您的问题,而不是偏离可能不必对特定问题多说的其他人。

标签: c timer posix real-time poisson


【解决方案1】:

您已经非常接近 vanilla Linux 所能提供的极限,而且已经超出了它所能保证的范围。将real-time patches 添加到您的内核并调整完全抢占将有助于在负载下为您提供更好的保证。我还将从您的时间关键代码中删除任何动态内存分配,如果它必须从 i/ o 缓存。我还会考虑从那台机器上删除交换以帮助保证性能。将处理器专用于您的任务将有助于防止上下文切换时间,但同样不能保证。

我还建议你小心那个级别的 sched_priority,你在 Linux 的各个重要部分之上,这可能会导致非常奇怪的效果。

【讨论】:

  • “将实时补丁添加到您的内核并针对完全抢占进行调整将有助于在负载下为您提供更好的保证。”你能指出我谈论这些项目的任何资源吗?我有嵌入式系统的 RT 知识,但从未将其应用到 Linux..
  • 有趣的是,使用完整的实时补丁实际上会降低应用程序的性能,并且提供的粒度比不使用它时的粒度要小。我猜这是因为通知我计时器已经“关闭”的调用现在被抢占了,因此传播到我的应用程序的速度不够快。这听起来合理吗?我已将 RT 优先级从 99 更改为 1 以尝试解决此问题,但没有影响。
  • 您可能会发现最好的情况是最坏的,但最坏的情况是更好的。在运行测试时,您是否将系统置于严重的 CPU/内存/IO 负载下?
【解决方案2】:

您从构建实时内核中获得的是更可靠的保证(即更低的最大延迟),即内核处理的 IO/定时器事件与将控制权传递给您的应用作为响应之间的时间。这是以较低的吞吐量为代价的,您可能会注意到最佳情况下的延迟时间增加了。

但是,使用 OS 计时器以高精度安排事件的唯一原因是,如果您害怕在等待下一个到期事件时循环消耗 CPU 周期。操作系统计时器(尤其是在 MS Windows 中)对于高粒度计时事件不可靠,并且非常依赖于您系统中可用的计时/HPET 硬件类型。

当我需要高度准确的事件调度时,我使用混合方法。首先,我测量了最坏情况下的延迟——即我请求睡眠的时间与睡眠后的实际时钟时间之间的最大差异。我们称这种差异为“D”。 (实际上,您可以在正常跑步期间即时执行此操作,方法是在每次睡觉时跟踪“D”,使用“D = (D*7 + lastD) / 8”之类的东西来生成时间平均值)。

那么永远不要在“N - D*2”之后请求睡眠,其中“N”是下一个事件的时间。在下一个事件的“D*2”时间内,进入一个自旋循环并等待“N”发生。

这会占用更多的 CPU 周期,但根据您需要的精度,您可以在自旋循环中使用“sched_yield()”,这对您的系统更友好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 1970-01-01
    相关资源
    最近更新 更多