[021] [RT-Thread学习笔记] CPU利用率计算

RT-Thread
学习笔记
CPU利用率统计
CPU利用率测试

RT-Thread版本:4.0.5
MCU型号:STM32F103RCT6(ARM Cortex-M3 内核)

1 CPU利用率统计

全速运行:不响应中断,也不去执行其他任务,就单纯让它在一个地方持续运行一段时间,这个值可以体现CPU的算力有多大。

  • total_count:单位时间内全速运行下的变量值,表现了单片机全速运行下,所能达到的最大值。
  • count:单位时间内空闲任务自加的变量值
  • cpu_usage :CPU利用率,cpu_usage = (total_count – count)/ total_count × 100 %
  • CPU_USAGE_CALC_TICK:cpu利用率计算周期
  • CPU_USAGE_LOOP:防止计数变量total_count/count溢出所加的循环次数,即原地自旋延长CPU每次计数消耗的时间

rt-thread官方例程中给出了计算方法:

#include <rtthread.h>
#include <rthw.h>

#define CPU_USAGE_CALC_TICK    10
#define CPU_USAGE_LOOP        100

static rt_uint8_t  cpu_usage_major = 0, cpu_usage_minor= 0;
static rt_uint32_t total_count = 0;

static void cpu_usage_idle_hook()
{
    
    
    rt_tick_t tick;
    rt_uint32_t count;
    volatile rt_uint32_t loop;

    if (total_count == 0)
    {
    
    
        /* get total count */
        rt_enter_critical();
        tick = rt_tick_get();
        while(rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
        {
    
    
            total_count ++;
            loop = 0;
            while (loop < CPU_USAGE_LOOP) loop ++;
        }
        rt_exit_critical();
    }

    count = 0;
    /* get CPU usage */
    tick = rt_tick_get();
    while (rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
    {
    
    
        count ++;
        loop  = 0;
        while (loop < CPU_USAGE_LOOP) loop ++;
    }

    /* calculate major and minor */
    if (count < total_count)
    {
    
    
        count = total_count - count;
        cpu_usage_major = (count * 100) / total_count;
        cpu_usage_minor = ((count * 100) % total_count) * 100 / total_count;
    }
    else
    {
    
    
        total_count = count;

        /* no CPU usage */
        cpu_usage_major = 0;
        cpu_usage_minor = 0;
    }
}

void cpu_usage_get(rt_uint8_t *major, rt_uint8_t *minor)
{
    
    
    RT_ASSERT(major != RT_NULL);
    RT_ASSERT(minor != RT_NULL);

    *major = cpu_usage_major;
    *minor = cpu_usage_minor;
}

void cpu_usage_init()
{
    
    
    /* set idle thread hook */
    rt_thread_idle_sethook(cpu_usage_idle_hook);
}

进入空闲线程时会调用cpu_usage_idle_hookcpu利用率计算空闲钩子函数,该函数主要分为三部分:

  • 计算total_count

用调度锁保护临界段,防止被其他线程抢占,让total_countCPU_USAGE_CALC_TICK周期内累加,测试cpu全速运算时的算力。(没用中断锁是因为需要利用systick中断来计数)

  • 计算count

不用调度锁保护,可以被其他线程抢占,即count为cpu空闲时的累加值

  • 计算major 和 minor

cpu_usage_major为利用率整数部分,cpu_usage_minor为利用率小数部分(两位),则利用率为cpu_usage_major·cpu_usage_minor%。如果count ≥ total_count,则说明这段时间CPU没有处理其他事情,基本都在空闲线程中做运算,即利用率视为0%

影响计算精度的问题:

  1. 调度锁只把调度器关了,中断依然可以正常抢占,如systick会1ms打断一次,如果一个线程执行时间超过1ms,则会被systick打断,那么本次计算时间就不准确了
  2. systick定时器本身也存在误差,精度在1ms左右,因此,在100毫秒的计算周期里面,有1% 的误差存在,在10毫秒的计算周期里面,误差10%
  3. 计算周期与线程占用cpu时间有关,如果占用时间接近CPU_USAGE_CALC_TICK,则需要将计算周期调大。如:计算周期5ms,当一个线程执行周期3ms,且误差可能在2ms左右(systick抢占误差+计时误差),则可能会超过计算周期,导致本轮计算溢出,下一个周期才会进入空闲线程进行闲时计数。
  4. 任务执行周期与计算周期共振问题。如一个线程执行周期为10ms,每次占用2.56ms,当空闲线程进行闲时计数时,执行到9.4ms时,被主线程抢占,此时执行了0.6ms后,本轮计算就结束了,导致最后结果为6%

image-20220327181547803

注意:线程执行周期 = 线程执行时间 + 系统延时(rt_thread_delay)

2 CPU利用率测试

在空闲线程初始化前设置cpu利用计算钩子函数:

int rtthread_startup(void)
{
    
    
	[...]
    cpu_usage_init();
    /* idle thread initialization */
    rt_thread_idle_init();
    [...]
}

直接在main线程中测试:

#define CPU_USAGE_CALC_TICK    100
#define CPU_USAGE_LOOP        100
int main(void)
{
    
    
    rt_uint8_t major, minor;
    while(1)
    {
    
    
        cpu_usage_get(&major, &minor);
        rt_kprintf("cpu usage: %u.%u%%  ", major, minor);
        rt_thread_mdelay(10);
    }
}

image-20220327182714308

  • CPU_USAGE_CALC_TICK计算周期改为10ms:

image-20220327182857915

cpu利用率明显降低,即任务执行周期与计算周期发生了共振。

  • CPU_USAGE_CALC_TICK计算周期改为1000ms:
    image-20220327183242780

与第一次测量差别不大。

  • 添加以下代码模拟占用cpu时间:
for (int i = 0; i < 10000; i++);

image-20220327184124559
参考:

END

猜你喜欢

转载自blog.csdn.net/kouxi1/article/details/123778645
今日推荐