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_hook cpu利用率计算空闲钩子函数,该函数主要分为三部分:
- 计算total_count
用调度锁保护临界段,防止被其他线程抢占,让total_count在CPU_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%
影响计算精度的问题:
- 调度锁只把调度器关了,中断依然可以正常抢占,如systick会1ms打断一次,如果一个线程执行时间超过1ms,则会被systick打断,那么本次计算时间就不准确了
- systick定时器本身也存在误差,精度在1ms左右,因此,在100毫秒的计算周期里面,有1% 的误差存在,在10毫秒的计算周期里面,误差10%
- 计算周期与线程占用cpu时间有关,如果占用时间接近CPU_USAGE_CALC_TICK,则需要将计算周期调大。如:计算周期5ms,当一个线程执行周期3ms,且误差可能在2ms左右(systick抢占误差+计时误差),则可能会超过计算周期,导致本轮计算溢出,下一个周期才会进入空闲线程进行闲时计数。
- 任务执行周期与计算周期共振问题。如一个线程执行周期为10ms,每次占用2.56ms,当空闲线程进行闲时计数时,执行到9.4ms时,被主线程抢占,此时执行了0.6ms后,本轮计算就结束了,导致最后结果为6%
注意:线程执行周期 = 线程执行时间 + 系统延时(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);
}
}
- 将
CPU_USAGE_CALC_TICK
计算周期改为10ms:
cpu利用率明显降低,即任务执行周期与计算周期发生了共振。
- 将
CPU_USAGE_CALC_TICK
计算周期改为1000ms:
与第一次测量差别不大。
- 添加以下代码模拟占用cpu时间:
for (int i = 0; i < 10000; i++);
参考:
END