文章描述基于Rhealstone的系统实时性的测量基准的框架--R-Rhealstone框架。
在嵌入式软件设计和集成中,实时多任务操作系统的性能分析是至关重要的,它需要保证应用的时间限制得到满足,即是运行时间不能超过应用的时间限制。为了选择满足用于特定应用的嵌入式系统的一个适当的操作系统,我们需要对操作系统服务进行分析。这些操作系统服务是由形成性能指标的参数确定的,既定的性能指标包括上下文切换时间、任务抢占时间、中断延迟时间、信号量混洗时间、死锁解除时间、信息传输延迟。
关于实时操作系统对性能指标进行分析,是为了选择满足用于特定应用的嵌入式系统的最优的操作系统。
Rhealstone是系统实时性的测量基准之一,Rhealstone性能基准程序是实时系统的六个关键操作的时间量进行操作,这六个关键操作是:上下文切换时间、任务抢占时间、中断延迟时间、信号量混洗时间、死锁解除时间、信息传输延迟。这六项操作作为Rhealstone的六个组件,每个组件被单独测量。然后将经验结果合并为单一的测量值,即是Rhealstone值。
序号 | 说明 |
---|---|
方式 1 | 通用Rhealstone |
方式 2 | 每个组件应用于具体应用程序的特定Rhealstone |
序号 | 说明 |
---|---|
缺点 1 | 测量的是平均时间,而不是最坏值 |
缺点 2 | 后的结论是加权平均值,没有给出确定权值的依据 |
#include "rst.h"
#include "rst_ipc.h"
#include "rst_btime.h"
static float loop_overhead = 0.0;
static float dir_overhead = 0.0;
static float telapsed = 0.0;
static uint32_t count1, count2;
static rst_task_id rst_task1 = NULL;
static rst_task_id rst_task2 = NULL;
static rst_task_attr rst_task1_attr = {
.name = "task1",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static rst_task_attr rst_task2_attr = {
.name = "task2",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static void rst_task2_func(void *arg);
static void rst_task1_func(void *arg)
{
rst_task_create(&rst_task2, rst_task2_func, NULL, &rst_task2_attr);
if(rst_task2 == NULL)
{
RST_LOGE("RST: task2 create failed");
rst_task_delete(NULL);
return;
}
/* Yield processor so second task can startup and run */
rst_task_yield();
for(count1 = 0; count1 < RST_BENCHMARKS_COUNT; count1++)
{
rst_task_yield();
}
rst_task_delete(NULL);
}
static void rst_task2_func(void *arg)
{
/* All overhead accounted for now, we can begin benchmark */
rst_benchmark_time_init();
for(count2 = 0; count2 < RST_BENCHMARKS_COUNT; count2++)
{
rst_task_yield();
}
telapsed = rst_benchmark_time_read();
RST_PRINT_TIME(
"R-Rhealstone: task switch time",
telapsed, /* Total time of all benchmarks */
(RST_BENCHMARKS_COUNT * 2) - 1, /* ( BENCHMARKS * 2 ) - 1 total benchmarks */
loop_overhead, /* Overhead of loop */
dir_overhead /* Overhead of rst_task_yield directive */
);
rst_task_delete(NULL);
}
rst_status rst_task_switch_init(void)
{
/* find overhead of routine (no task switches) */
rst_benchmark_time_init();
for(count1 = 0; count1 < RST_BENCHMARKS_COUNT; count1 ++)
{
}
for(count1 = 0; count1 < RST_BENCHMARKS_COUNT; count1 ++)
{
}
loop_overhead = rst_benchmark_time_read();
/* find overhead of rtems_task_wake_after call (no task switches) */
rst_benchmark_time_init();
rst_task_yield();
dir_overhead = rst_benchmark_time_read();;
rst_task_create(&rst_task1, rst_task1_func, NULL, &rst_task1_attr);
if(rst_task1 == NULL)
{
RST_LOGE("RST: task1 create failed");
return RST_ERROR;
}
return RST_OK;
}
#include "rst.h"
#include "rst_ipc.h"
#include "rst_btime.h"
static float loop_overhead = 0.0;
static float switch_overhead = 0.0;
static float telapsed = 0.0;
static uint32_t count;
static rst_task_id rst_task1 = NULL;
static rst_task_id rst_task2 = NULL;
static rst_task_attr rst_task1_attr = {
.name = "task1",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 3,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 3,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static rst_task_attr rst_task2_attr = {
.name = "task2",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static void rst_task2_func(void *arg);
static void rst_task1_func(void *arg)
{
/* Start up task2, get preempted */
rst_task_create(&rst_task2, rst_task2_func, NULL, &rst_task2_attr);
if(rst_task2 == NULL)
{
RST_LOGE("RST: task2 create failed");
rst_task_delete(NULL);
return;
}
switch_overhead = rst_benchmark_time_read();
rst_benchmark_time_init();
/* Benchmark code */
for(count = 0; count < RST_BENCHMARKS_COUNT; count++)
{
rst_task_resume(rst_task2); /* Awaken task2, preemption occurs */
}
rst_task_delete(NULL);
}
static void rst_task2_func(void *arg)
{
/* Find overhead of task switch back to task1 (not a preemption) */
rst_benchmark_time_init();
rst_task_suspend(rst_task2);
/* Benchmark code */
for(; count < RST_BENCHMARKS_COUNT - 1;)
{
rst_task_suspend(rst_task2);
}
telapsed = rst_benchmark_time_read();
RST_PRINT_TIME(
"R-Rhealstone: task preempt time",
telapsed, /* Total time of all benchmarks */
RST_BENCHMARKS_COUNT - 1, /* BENCHMARKS - 1 total benchmarks */
loop_overhead, /* Overhead of loop */
switch_overhead /* Overhead of task switch back to task1 */
);
rst_task_delete(NULL);
}
rst_status rst_task_preempt_init(void)
{
/* Find loop overhead */
rst_benchmark_time_init();
for(count = 0; count < ((RST_BENCHMARKS_COUNT * 2) - 1); count++)
{
}
loop_overhead = rst_benchmark_time_read();
rst_task_create(&rst_task1, rst_task1_func, NULL, &rst_task1_attr);
if(rst_task1 == NULL)
{
RST_LOGE("RST: task1 create failed");
return RST_ERROR;
}
return RST_OK;
}
#include "rst.h"
#include "rst_ipc.h"
#include "rst_btime.h"
static float timer_overhead = 0.0;
static float isr_enter_time = 0.0;
static rst_task_id rst_task1 = NULL;
static rst_task_attr rst_task1_attr = {
.name = "task1",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_LOWEST_PRIORITY + 1,
#else
.priority = RST_TASK_LOWEST_PRIORITY - 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static void rst_isr_handler(void *param)
{
isr_enter_time = rst_benchmark_time_read();
}
static void rst_task1_func(void *arg)
{
rst_isr_install(RST_ISR_NUM, rst_isr_handler, NULL);
/* Benchmark code */
rst_benchmark_time_init();
/* goes to Isr_handler */
rst_isr_trigger(RST_ISR_NUM);
RST_PRINT_TIME(
"R-Rhealstone: interrupt latency time",
isr_enter_time,
1, /* Only Rhealstone that isn't an average */
timer_overhead,
0
);
rst_task_delete(NULL);
}
rst_status rst_interrupt_latency_init(void)
{
rst_task_create(&rst_task1, rst_task1_func, NULL, &rst_task1_attr);
if(rst_task1 == NULL)
{
RST_LOGE("RST: task1 create failed");
return RST_ERROR;
}
rst_benchmark_time_init();
rst_benchmark_time_read();
rst_benchmark_time_init();
timer_overhead = rst_benchmark_time_read();
return RST_OK;
}
#include "rst.h"
#include "rst_ipc.h"
#include "rst_btime.h"
static float switch_overhead = 0.0;
static float telapsed = 0.0;
static uint32_t count = 0;
static uint32_t sem_exe = 1;
static rst_task_id rst_task1 = NULL;
static rst_task_id rst_task2 = NULL;
static rst_sem_id rst_sem = NULL;
static rst_task_attr rst_task1_attr = {
.name = "task1",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static rst_task_attr rst_task2_attr = {
.name = "task2",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static void rst_task2_func(void *arg);
static void rst_task1_func(void *arg)
{
/* Start up task2, yield so it can run */
rst_task_create(&rst_task2, rst_task2_func, NULL, &rst_task2_attr);
if(rst_task2 == NULL)
{
RST_LOGE("RST: task2 create failed");
rst_task_delete(NULL);
return;
}
rst_task_yield();
/* Benchmark code */
for ( ; count < RST_BENCHMARKS_COUNT; ) {
if ( sem_exe == 1 )
{
rst_sem_lock(rst_sem, (rst_time_t)RST_WAIT_FOREVER);
rst_task_yield();
rst_sem_unlock(rst_sem);
rst_task_yield();
}
}
rst_task_delete(NULL);
}
static void rst_task2_func(void *arg)
{
/* Benchmark code */
rst_benchmark_time_init();
for(count = 0; count < RST_BENCHMARKS_COUNT; count++)
{
if(sem_exe == 1)
{
rst_sem_lock(rst_sem, (rst_time_t)RST_WAIT_FOREVER);
rst_task_yield();
rst_sem_unlock(rst_sem);
rst_task_yield();
}
}
telapsed = rst_benchmark_time_read();
if(sem_exe == 0)
{
switch_overhead = telapsed;
}
else
{
RST_PRINT_TIME(
"R-Rhealstone: senaphore shuffle time",
telapsed, /* Total time of all benchmarks */
(RST_BENCHMARKS_COUNT * 2), /* Total number of times deadlock broken*/
switch_overhead, /* Overhead of loop and task switches */
0
);
}
rst_task_delete(NULL);
}
rst_status rst_semaphore_shuffle_init(void)
{
rst_sem = rst_sem_create(1);
if(rst_sem == NULL)
{
RST_LOGE("RST: sem create failed");
return RST_ERROR;
}
__RESTART:
sem_exe = !sem_exe;
/* Get time of benchmark with no semaphore shuffling */
rst_task_create(&rst_task1, rst_task1_func, NULL, &rst_task1_attr);
if(rst_task1 == NULL)
{
RST_LOGE("RST: task1 create failed");
rst_sem_delete(rst_sem);
return RST_ERROR;
}
/* Get time of benchmark with semaphore shuffling */
if(sem_exe == 0)
{
goto __RESTART;
}
return RST_OK;
}
#include "rst.h"
#include "rst_ipc.h"
#include "rst_btime.h"
static float switch_overhead = 0.0;
static float lock_overhead = 0.0;
static float telapsed = 0.0;
static uint32_t count = 0;
static uint32_t sem_exe = 1;
static rst_task_id rst_task1 = NULL;
static rst_task_id rst_task2 = NULL;
static rst_task_id rst_task3 = NULL;
static rst_sem_id rst_sem = NULL;
static rst_task_attr rst_task1_attr = {
.name = "task1",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static rst_task_attr rst_task2_attr = {
.name = "task2",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 3,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 3,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static rst_task_attr rst_task3_attr = {
.name = "task3",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 5,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 5,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static void rst_task1_func(void *arg)
{
/* All tasks have had time to start up once TA01 is running */
/* Benchmark code */
rst_benchmark_time_init();
for(count = 0; count < RST_BENCHMARKS_COUNT; count++)
{
if(sem_exe == 1)
{
/* Block on call */
rst_sem_lock(rst_sem, (rst_time_t)RST_WAIT_FOREVER);
/* Release semaphore immediately after obtaining it */
rst_sem_unlock(rst_sem);
}
/* Suspend self, go to task2 */
rst_task_suspend(rst_task1);
}
telapsed = rst_benchmark_time_read();
if(sem_exe == 0)
{
switch_overhead = telapsed;
}
else
{
RST_PRINT_TIME(
"R-Rhealstone: deadlock break time",
telapsed, /* Total time of all benchmarks */
RST_BENCHMARKS_COUNT, /* Total number of times deadlock broken*/
switch_overhead, /* Overhead of loop and task switches */
lock_overhead
);
}
rst_task_delete(NULL);
}
static void rst_task2_func(void *arg)
{
/* Start up task1, get preempted */
rst_task_create(&rst_task1, rst_task1_func, NULL, &rst_task1_attr);
if(rst_task1 == NULL)
{
RST_LOGE("RST: task1 create failed");
rst_task_delete(NULL);
return;
}
/* Benchmark code */
for( ; count < RST_BENCHMARKS_COUNT; )
{
/* Suspend self, go to task1 */
rst_task_suspend(rst_task2);
/* Wake up task1, get preempted */
rst_task_resume(rst_task1);
}
rst_task_delete(NULL);
}
static void rst_task3_func(void *arg)
{
if(sem_exe == 1)
{
/* Low priority task holds mutex */
rst_sem_lock(rst_sem, (rst_time_t)RST_WAIT_FOREVER);
}
/* Start up task2, get preempted */
rst_task_create(&rst_task2, rst_task2_func, NULL, &rst_task2_attr);
if(rst_task2 == NULL)
{
RST_LOGE("RST: task2 create failed");
rst_task_delete(NULL);
return;
}
for( ; count < RST_BENCHMARKS_COUNT; )
{
if(sem_exe == 1)
{
/* Preempted by task1 upon release */
rst_sem_unlock(rst_sem);
/* Prepare for next Benchmark */
rst_sem_lock(rst_sem, (rst_time_t)RST_WAIT_FOREVER);
}
/* Wake up task2, get preempted */
rst_task_resume(rst_task2);
}
rst_task_delete(NULL);
}
rst_status rst_deadlock_break_init(void)
{
rst_sem = rst_sem_create(1);
if(rst_sem == NULL)
{
RST_LOGE("RST: sem create failed");
return RST_ERROR;
}
/* find overhead of obtaining semaphore */
rst_benchmark_time_init();
rst_sem_lock(rst_sem, (rst_time_t)RST_WAIT_FOREVER);
lock_overhead = rst_benchmark_time_read();
rst_sem_unlock(rst_sem);
__RESTART:
sem_exe = !sem_exe;
/* Get time of benchmark with no semaphores involved, i.e. find overhead */
rst_task_create(&rst_task3, rst_task3_func, NULL, &rst_task3_attr);
if(rst_task3 == NULL)
{
RST_LOGE("RST: task3 create failed");
rst_sem_delete(rst_sem);
return RST_ERROR;
}
/* Get time of benchmark with semaphores */
if(sem_exe == 0)
{
goto __RESTART;
}
rst_sem_delete(rst_sem);
return RST_OK;
}
#include "rst.h"
#include "rst_ipc.h"
#include "rst_btime.h"
#define RST_QUEUE_BUFF_SIZE 4
static float loop_overhead = 0.0;
static float receive_overhead = 0.0;
static float telapsed = 0.0;
static uint32_t count;
static rst_task_id rst_task1 = NULL;
static rst_task_id rst_task2 = NULL;
static rst_queue_id rst_queue = NULL;
static int queue_buff[RST_QUEUE_BUFF_SIZE] = {0};
static rst_task_attr rst_task1_attr = {
.name = "task1",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 3,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 3,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static rst_task_attr rst_task2_attr = {
.name = "task2",
#if RST_BIG_NUM_HIGH_PRIORITY
.priority = RST_TASK_HIGHEST_PRIORITY - 1,
#else
.priority = RST_TASK_HIGHEST_PRIORITY + 1,
#endif
.stack_size = RST_TASK_STACK_SIZE,
};
static void rst_task2_func(void *arg);
static void rst_task1_func(void *arg)
{
/* Put a message in the queue so recieve overhead can be found. */
rst_queue_send(rst_queue,
(const void *)queue_buff,
(uint32_t)sizeof(queue_buff),
(rst_time_t)RST_WAIT_FOREVER);
/* Start up second task, get preempted */
rst_task_create(&rst_task2, rst_task2_func, NULL, &rst_task2_attr);
if(rst_task2 == NULL)
{
RST_LOGE("RST: task2 create failed");
rst_task_delete(NULL);
return;
}
for(; count < RST_BENCHMARKS_COUNT; count++)
{
rst_queue_send(rst_queue,
(const void *)queue_buff,
(uint32_t)sizeof(queue_buff),
(rst_time_t)RST_WAIT_FOREVER);
}
rst_task_delete(NULL);
}
static void rst_task2_func(void *arg)
{
/* find recieve overhead - no preempt or task switch */
rst_benchmark_time_init();
rst_queue_recv(rst_queue, (void *)queue_buff,
(uint32_t)sizeof(queue_buff));
receive_overhead = rst_benchmark_time_read();
/* Benchmark code */
rst_benchmark_time_init();
for(count = 0; count < RST_BENCHMARKS_COUNT - 1; count++)
{
rst_queue_recv(rst_queue, (void *)queue_buff,
(uint32_t)sizeof(queue_buff));
}
telapsed = rst_benchmark_time_read();
RST_PRINT_TIME(
"R-Rhealstone: message latency time",
telapsed, /* Total time of all benchmarks */
RST_BENCHMARKS_COUNT - 1, /* BENCHMARKS - 1 total benchmarks */
loop_overhead, /* Overhead of loop */
receive_overhead /* Overhead of recieve call and task switch */
);
rst_task_delete(NULL);
}
rst_status rst_message_latency_init(void)
{
rst_queue = rst_queue_create(sizeof(queue_buff), 1);
if(rst_queue == NULL)
{
RST_LOGE("RST: queue create failed");
return RST_ERROR;
}
rst_benchmark_time_init();
for(count = 0; count < (RST_BENCHMARKS_COUNT - 1); count++)
{
}
loop_overhead = rst_benchmark_time_read();
rst_task_create(&rst_task1, rst_task1_func, NULL, &rst_task1_attr);
if(rst_task1 == NULL)
{
RST_LOGE("RST: task1 create failed");
rst_queue_delete(rst_queue);
return RST_ERROR;
}
return RST_OK;
}
项 | 说明 |
---|---|
芯片 | 芯片型号:stm32f401芯片架构:Cortex-M4 主频:84 MHz |
开发环境 | KEIL 5.x |
工具链 | ARMCC |
对比项 | RT-Thread | LiteOS | FreeRTOS | TobudOS |
---|---|---|---|---|
上下文切换 | 2.594596 us | 6.739740 us | 1.049049 us | 2.343343 |
任务抢占 | 7.360721 us | 7.603206 us | 2.715431 us | 4.523046 us |
中断延迟 | 2.000000 us | 1.000000 us | 1.000000 us | 1.000000 us |
信号量混洗 | 23.829000 us | 25.588000 us | 19.496000 us | 18.451000 us |
死锁解除 | 18.108000 us | 18.074000 us | 21.522000 us | 31.606000 us |
信息传输延迟 | 7.749499 us | 7.390782 us | 7.298597 us | 3.446894 us |
链接路径:https://github.com/RiceChen0/r-rhealstone.git
说明:该框架目前已经适配作为RT-Thread的软件包,可以通过软件包体验其功能
本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!