#在条件宽裕的情况下,购买第三方工具自然对于测试执行时间以及负载率等极为方便,建议采用此种方式
负载率 = 占用时间 /(占用时间 + 空闲时间)
#简单来说,就是工作时间占用总时间的比例,比如说你需要工作12小时,就可以说你负载率为50%。这可量化反映当前使用情况
从上面可以看出来计算负载率,需要解决两个问题:
1)、必须可量化得获取占用时间
2)、得存在可靠的计时方式
时间统计要求准确,因此对于我们来说,比较合适的就是硬件定时器里面计数(如英飞凌中的STM、ATOM等)。
简单示例(以TC397为例):
1)、存在6核,每个核心存在各自定时器;
2)、建议和系统使用的定时器使用同一个;
3)、同时也可使用其他配置的定时器
#如下,调用时可采用此类方式,“Count = *AppLoad_Timer[Index]
”
const uint32 *AppLoad_Timer[APPLOAD_CORENUMBER] =
{
(uint32 *)&STM0_TIM0.U, /*Core0定时寄存器相应地址*/
(uint32 *)&STM1_TIM0.U,
(uint32 *)&STM2_TIM0.U,
(uint32 *)&STM3_TIM0.U,
(uint32 *)&STM4_TIM0.U,
(uint32 *)&STM5_TIM0.U
};
#建议直接从相关定时器计数的寄存器地址获取,切记不可使用软件定时器(其所在任务存在偏移的情况,因此会导致计时不准确)
在任务执行过程中,会存在任务切换(中断打断任务等),因此直接在任务开始处与结束的位置来表示开始与结束会造成实际执行时间大于任务实际执行时间。
可以参考下“AutosarOS:错误处理、跟踪与调试(博文)”定义,在任务切换(上下文切换)时,可以进入PreTaskHook、PostTaskHook,因此在此Hook函数中进行处理,可以通过处理得到准确的Task执行时间。
简单示例:
1)、通过在"PostTaskHook"中获取时间计数,可知相应的退出任务的时间点
2)、类似于步骤1),通过在"PreTaskHook"中获取时间计数,可知相应的进入任务的时间点
3)、根据进入任务的时间点、退出任务的时间点,可以计算出任务的运行时间、被打断时间等
void AppLoad_PostTaskHook(void) /*此函数须在"PostTaskHook"中进行调用*/
{
uint8 CoreId;
uint16 TaskId;
CoreId = AppLoad_GetCoreId(); /*获取当前运行此Hook函数的内核ID: 函数名经过封装(可自己查找相应的API)*/
AppLoad_GetTaskId(&TaskId); /*获取退出的函数任务ID*/
switch (CoreId)
{
case COREID_CORE0:
{
/*此处可以获取相应任务的退出时间,用于计算运行时间*/
}
break;
....../*可能存在多核的情况,可根据实际情况进行扩展*/
default:
break;
}
}
对于具体的功能执行时间存在以下几方面问题:
1)、无法找到进入或者退出时间点(中断对其时间测量不可忽视)
2)、需要评估执行时间相对于所在任务可用的时间片的占比(调度表形式调用极为重要,决定了会不会影响任务偏移以及偏移后会不会跳过执行等)
#以时间片形式进行处理时,需要保障该任务存在偏移在一定范围内(确保不会触发时间保护),因此也需要确保单个任务的负载率控制在一定范围
绝对执行时间 = 执行结束时间 - 执行开始时间
该参数反映了该功能函数的绝对执行时间,可用于该函数在进行任务分配时的依据。
简单示例
结合"PreTaskHook"以及"PostTaskHook"说明通过Hook函数进行时间计算的过程:
1)、如图可知,可在"PreTaskHook"中获取进入任务时间点,可在"PostTaskHook"获取退出任务时间点
2)、因此需要知道该任务的执行时间只需要将图中绿色的双箭头部分时间计算出来即可
#注意定时器位数,溢出时需要特殊处理(切记计数时需要处理此类情况)
static void AppLoad_CalcPostUserTaskTime(uint8 CoreId, uint16 TaskId)/*需要传入内核ID(哪个定时器时间)、任务ID(是否是测试的任务触发)*/
{
/*记录相应的任务退出时间节点*/
}
static void AppLoad_CalcPreUserTaskTime(uint8 CoreId, uint16 TaskId)/*需要传入内核ID(哪个定时器时间)、任务ID(是否是测试的任务触发)*/
{
/*记录相应的任务退出时间节点*/
/*计算在任务中真实运行时间*/
}
#在进行比较小的执行时间计算时,建议禁用中断进行测试(此方式可以更准确判断出代码执行时间,避免其他方式带来的时间开销(任务切换等)),避免中断对测试结果影响
相对执行时间 = 功能函数执行时间 / 任务时间片时间
相对执行时间反映了功能函数执行时间占用任务的可用时间比例,可作为任务执行时间优化的重要依据(以上针对的是调度表存在时间片的情况下),此处不做详细说明(此方式在本人接触的项目中未大量使用)
在测试过程中或者真实工况条件下,若直接获取相应函数进入与退出时时间记录,由于通信、ADC采集、定时器等可能产生的中断影响,函数/任务实际执行时间应大于真实执行时间。
函数执行时间 = 中断执行时间 + 函数真实执行时间
#因此为了正确准确评价函数执行时间可在执行过程中屏蔽中断“EnableAllInterrupts/ DisableAllInterrupts”,具体可参考AutosarOS:操作系统基本概念
首先说下周期偏移带来的常见影响:
1)、窗狗喂狗超时或者过早,导致软件复位
2)、通信周期波动较大(如CAN的周期报文)
任务执行时间 = 本次任务执行时间时间计数 - 上次任务执行时间时间计数
简单代码示例:
void AppLoad_Get_Func_Cycle(uint8 AppLoad_Func_Index)
{
/*1)、记录当前定时器值*/
/*2)、计算当前任务周期、平均值*/
/*3)、记录最大值、最小值*/
}
typedef struct
{
uint32 Period_CycleCnt;/*用于计算平均值*/
float Period_CurValue;/*记录实时周期,可反应周期变化细节及趋势等信息*/
float Period_MinValue;/*记录最小值,可反应偏移量*/
float Period_MaxValue;/*记录最大值,可反应偏移量*/
float Period_AverValue;/*记录平均值*/
uint64 Period_TotalValue;/*用于计算平均值*/
}st_AppLoad_UserFunc_PeriodDefine;
#须对周期比较敏感的任务进行周期测试,记录周期偏移最大值,评估周期偏移是否在可接受范围内以及是否会带来软件bug(如复位、报文超时、甚至某些逻辑未执行等等)
负载率 = 执行任务时间 / (执行任务时间 + 空闲时间)
此处与章节2.1类似,不过需要把计算的对象替换成"IdleTask",只要计算出该任务的执行时间,则可反过来计算出来中断以及其他任务的执行时间
#注意事项:
1)、可选择一段时间为计时周期,如2s或者更高(最好为周期任务最小公倍数的倍数)
2)、执行时间为此段时间内所有任务执行时间的总和
函数负载率贡献 = 函数执行时间 / 函数执行周期
此概念类似于CAN总线上报文负载率,比如10ms(其他周期一样)的标准报文,在知道波特率的情况下,它对于总线负载率的增加是一定的。因此知道每个任务对于负载率的贡献,它们之和就为整体负载率
void AppLoad_CalculateUserTask(void)
{
/*通过章节2.1得到的时间,以及相应的任务周期即可计算任务负载率*/
}
#注意事项:
1)、负载率为所有函数负载率贡献之和
2)、注意在函数中存在函数周期倍数的执行方式,周期不应以函数为其为准,应该以里面调用的函数的最大周期作为该函数的周期
之前写的文档丢了,这次凭印象只写了大概,大家见谅。后续想起来了再进行补充,有时间把相关的代码也可以进行补充下