c++ Linux抢占时间:SCHED_OTHER的性能优于SCHED_FIFO,为什么?

wnavrhmk  于 2023-01-14  发布在  Linux
关注(0)|答案(1)|浏览(102)

我正在试验Raspberry Pi 3/4的实时功能。我编写了以下C++程序进行测试。

// Compile with:
// g++ realtime_task.cpp -o realtime_task -lrt && sudo setcap CAP_SYS_NICE+ep realtime_task

#include <cstdio>
#include <sched.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdbool>
#include <chrono>
#include <algorithm> 

using namespace std;
using namespace chrono;
using namespace chrono_literals;

int main(int argc, char **argv)
{   
    // allocate this process to the 4th core (core 3)
    pid_t pid = getpid();
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(3, &cpuset);
    int result = 0;
    result = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset);
    if (result != 0)
    {
        perror("`sched_setaffinity` failed");
    }

    struct sched_param param;

    // Use SCHED_FIFO
    param.sched_priority = 99;
    result = sched_setscheduler(pid, SCHED_FIFO, &param);

    // Use SCHED_OTHER
    // param.sched_priority = 0;
    // result = sched_setscheduler(pid, SCHED_OTHER, &param);

    if (result != 0)
    {
        perror("`sched_setscheduler` failed");
    }

    uint32_t count = 0;
    uint32_t total_loop_time_us = 0;
    uint32_t min_loop_time_us = numeric_limits<uint32_t>::max();
    uint32_t avg_loop_time_us = 0;
    uint32_t max_loop_time_us = 0;
    
    while(true)
    {
        count++;

        auto start = steady_clock::now();

        auto loop_time_us = duration_cast<microseconds>(steady_clock::now() - start).count();
        while(loop_time_us < 500)
        { 
            loop_time_us = duration_cast<microseconds>(steady_clock::now() - start).count();
        }

        min_loop_time_us = min((uint32_t)loop_time_us, (uint32_t)min_loop_time_us);
        total_loop_time_us += loop_time_us;
        avg_loop_time_us = total_loop_time_us / count;
        max_loop_time_us = max((uint32_t)loop_time_us, (uint32_t)max_loop_time_us);

        if ((count % 1000) == 0)
        {
            printf("%u %u %u\r", min_loop_time_us, avg_loop_time_us, max_loop_time_us);
            fflush(stdout);
        }
    }

    return 0;
}

我已经用PREEMPT_RT补丁修补了内核。uname报告5.15.84-v8+ #1613 SMP PREEMPT,一切运行正常。
内核命令行参数有isolcpus=3 irqaffinity=0-2来隔离第4个内核(core 3)并为上面的程序保留它,我可以在 htop 中看到我的程序是唯一运行在第4个内核(core 3)上的进程。
使用SCHED_FIFO策略时,它报告以下最小、平均和最大循环时间...

MIN AVG MAX
500 522 50042

..和 htop 报告:

CPU▽ CTXT     PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
  3     1   37238 pi         RT   0  4672  1264  1108 R 97.5  0.0  0:07.57 ./realtime_task

使用SCHED_OTHER策略时,它报告以下最小、平均和最大循环时间...

MIN AVG MAX
500 500 524

..和 htop 报告:

CPU▽ CTXT     PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
  3     0   36065 pi         20   0  4672  1260  1108 R 100.  0.0  1:30.16 ./realtime_task

这和我期望的正好相反。我期望SCHED_FIFO给予我更低的最大循环时间和更少的上下文切换。为什么我会得到这些结果呢?

eoxn13cs

eoxn13cs1#

我在这里猜测一下。如果你运行SCHED_OTHER,但是把你的进程的优先级设置为非默认值,它会把这个进程提升到任何其他默认进程之上。所以你的任务基本上是一个其他所有其他的任务,因此占用cpu的时间会更长。这是文档所读到的:https://man7.org/linux/man-pages/man7/sched.7.html
计划其他:默认Linux分时调度SCHED_OTHER只能用于静态优先级0(即实时策略下的线程始终优先于SCHED_OTHER进程)。SCHED_OTHER是标准Linux分时调度程序,适用于不需要特殊实时机制的所有线程。

相关问题