Оценка скорости реакции RTOS на события

от автора

продолжение темы RTOS, начало см.
https://habr.com/ru/posts/1060466/
https://habr.com/p/1062346/

Получив результат, а именно, запуск SMP версии RTOS, задался вопросом, а какая же плата в виде ухудшения временнЫх характеристик взимается за межъядерное взаимодействие сервисов RTOS?

Достал из пыльного чулана давно написанный тест производительнсти сервисов RTOS и причесал с целью использования в одно- и много-ядерном режиме.

спойлер:

os_perf_test.c:

#include "..\H\typedef.h"#include "RTT\RTT_ex.h"#include "osal.h"#include "osal_api.h"#include "os_perf_timer.h"//perfomance test for OS//config: ============================================================// -------------------------------------------------------------------// ०Ё¬ ®¤­®п¤Ґа­л© (UNICORE_MODE) Ё«Ё ¬­®Ј®п¤Ґа­л© SMP (MPCORE_MODE)// § ¤ Ґвбп #define ў Їа®ҐЄвҐ !!!!// -------------------------------------------------------------------//зЁб«® Їа®е®¤®ў ¤«п гб।­Ґ­Ёп १г«мв в #define TEST_PASS_NUM           64//ўЄ«о祭Ё вҐбв®ў Є®­ЄаҐв­ле бҐаўЁб®ў OS#define PERFTEST_QUEUE#define PERFTEST_EVF#define PERFTEST_SEM#define PERFTEST_MTX//०Ё¬л бЎ®а  бв вЁбвЁЄЁ Їа®Ё§ў®¤ЁвҐ«м­®бвЁ//  а §аҐиЁвм бзҐвзЁЄЁ PMU#define PMU_MODE_ENABLE//  а §аҐиЁвм в ©¬Ґа Cortex-A7#define TRM_MODE_ENABLE//  а §аҐиЁвм ¤®Ї®«­ЁвҐ«м­лҐ ¬ҐваЁЄЁ PMU#define PMU_METRIC_ENABLE//з бв®в  в ©¬Ґа  Ђ7#define TRM_FREQ_MHZ    24//§ ЇгбЄ вм ­  а §­ле п¤а е//#define DIFFERENT_CORES//config: ============================================================//ў§ Ё¬®Ў«®ЄЁа®ўЄЁ Є®­дЁЈга жЁЁ#ifdef MPCORE_MODE  #ifdef DIFFERENT_CORES    //ўЄ«оз Ґ¬ § ¬Ґа в®«мЄ® Ї® в ©¬Ґаг    #undef PMU_MODE_ENABLE    #define TRM_MODE_ENABLE  #endif#endif#ifndef PMU_MODE_ENABLE  #undef PMU_METRIC_ENABLE#endifextern DWORD getCPU_clk(void);extern DWORD OS_BSP_getCPUClock(void);extern WORD dig_itoa(BYTE *dst,int v,DWORD off,BYTE i,BYTE f,BYTE signMode);enum {PERF_QU_SID,PERF_EVF_SID,PERF_SEM_SID,PERF_MTX_SID,PERF_SID_TOTAL};enum _TEST_EVFS {QU_MEASURE=1,EVF_MEASURE=2,SEM_MEASURE=4,MTX_MEASURE=8};#define EVF_PERFTEST 1typedef struct _OS_PERF_DATA_ENTRY{DWORD c_min;            //pmu cyclesDWORD c_max;DWORD c_total;DWORD i_min;            //pmu instructionsDWORD i_max;DWORD i_total;DWORD t_min;            //trm ticksDWORD t_max;DWORD t_total;DWORD pmu_M1_total;     //pmu metric1DWORD pmu_M2_total;     //pmu metric2}OS_PERF_DATA_ENTRY;typedef struct _OS_PERF_DATA{DWORD pass;OS_PERF_DATA_ENTRY pdEntry[PERF_SID_TOTAL];}OS_PERF_DATA;typedef struct _OS_PERF_COUNTERS{DWORD pmuM1[PERF_SID_TOTAL];DWORD pmuM2[PERF_SID_TOTAL];DWORD cyc[PERF_SID_TOTAL];DWORD instr[PERF_SID_TOTAL];QWORD tt[PERF_SID_TOTAL];}OS_PERF_COUNTERS;//RTOS objects -------------------------------------------//#pragma default_variable_attributes = @ "DATA_NO_CACHE"//#pragma default_variable_attributes = @ "SRAM"#pragma default_variable_attributes=@ "RTOS_USERDATA"  #define TEST1_PRIORITY        (PRIO_LVL 15)  #define TEST2_PRIORITY        (PRIO_LVL 16)  #define TEST3_PRIORITY        (PRIO_LVL 17)  #define TEST1_QUEUE_LEN       8  #define TEST1_QUEUE_MSGSIZE   1  #define TEST1_STACK_SIZE      128  #define TEST2_STACK_SIZE      128  #define TEST3_STACK_SIZE      128  #pragma data_alignment=64  OS_QUEUE_CB   TEST1Queue;  #pragma data_alignment=64  OS_EVENTFLAGS_GROUP_CB TEST1Evf;  #pragma data_alignment=64  OS_MUTEX_CB TEST1Mtx;  #pragma data_alignment=64  OS_SEMAPHORE_CB TEST1Sem;  #pragma data_alignment=64  OS_TASK_CB    TEST1Thread;  #pragma data_alignment=64  OS_TASK_CB    TEST3Thread;  #pragma data_alignment=64  DWORD TEST1QueueData[TEST1_QUEUE_LEN*TEST1_QUEUE_MSGSIZE];  #pragma data_alignment=64  OS_TASK_STACK TEST1ThreadStack[TEST1_STACK_SIZE];  #pragma data_alignment=64  OS_TASK_CB    TEST2Thread;  #pragma data_alignment=64  OS_TASK_STACK TEST2ThreadStack[TEST2_STACK_SIZE];  #pragma data_alignment=64  OS_TASK_STACK TEST3ThreadStack[TEST3_STACK_SIZE];  #pragma data_alignment=64  OS_EVENTFLAGS_GROUP_CB TEST1SrvEvf;  //counters --------------  #pragma data_alignment=64  OS_PERF_COUNTERS cnt1;  DWORD errcnt[PERF_SID_TOTAL];  DWORD sid1;  #pragma data_alignment=64  OS_PERF_COUNTERS cnt2;  DWORD sid2;#pragma default_variable_attributes=//counters -------------------------------------------------#pragma default_variable_attributes = @ "DATA_NO_CACHE"  /*  //counters --------------  #pragma data_alignment=64  OS_PERF_COUNTERS cnt1;  #pragma data_alignment=64  OS_PERF_COUNTERS cnt2;  DWORD sid2;  #pragma data_alignment=64  DWORD errcnt[PERF_SID_TOTAL];  DWORD sid1;  */#pragma default_variable_attributes=OS_PERF_DATA OSPerfData;static void clearPerfData(void){OSPerfData.pass=0;for(DWORD i=0;i<PERF_SID_TOTAL;i++)  {  OSPerfData.pdEntry[i].c_min=0xFFFFFFFF;  OSPerfData.pdEntry[i].c_max=0;  OSPerfData.pdEntry[i].c_total=0;  OSPerfData.pdEntry[i].i_min=0xFFFFFFFF;  OSPerfData.pdEntry[i].i_max=0;  OSPerfData.pdEntry[i].i_total=0;  OSPerfData.pdEntry[i].t_min=0xFFFFFFFF;  OSPerfData.pdEntry[i].t_max=0;  OSPerfData.pdEntry[i].t_total=0;  OSPerfData.pdEntry[i].pmu_M1_total=0;  OSPerfData.pdEntry[i].pmu_M2_total=0;  }}void srtrcpyfill(BYTE *dst,BYTE *src,DWORD len){DWORD i,srclen=strlen((char *)src);for(i=0;i<srclen;i++)dst[i]=src[i];for(;i<len;i++)dst[i]=' ';}static BYTE *servName[PERF_SID_TOTAL]={"queue","eventFlag","semaphore","mutex"};//                0         1         2         3         4         5         6         7//                0.........0....5....0.2.......01......8.0.......8.0......7..0.....6...0...4static BYTE *ms5="xxxxxxxxxxxx   Cortex-A7 TRM result             xx.xxx   xx.xxx   xx.xxx  \r\n";#ifndef PMU_MODE_ENABLEstatic BYTE *ms4="service_name                                    min[uS]  max[uS]  avg[uS] \r\n";#endif#ifdef PMU_MODE_ENABLEstatic BYTE *ms3="PMU metrics    c1=xxxxxx       c2=yyyyyy \r\n";static BYTE *ms2="xxxxxxxxxxxx   yyyyyy/zzzzzz   yyyyyy/zzzzzz    xx.xxx   xx.xxx   xx.xxx  xx.xxx  \r\n";static BYTE *ms1="service_name   min[cyc/inst]   max[cyc/inst]    min[uS]  max[uS]  avg[uS] cpi     \r\n";#endifstatic BYTE *ms0=" CPUclk=XXXXMHz\r\n";static void ShowPerfomanceDataHelper(OS_PERF_DATA *pd,DWORD sid,DWORD cpuclk,DWORD trmclk){OS_PERF_DATA_ENTRY *pde=&pd->pdEntry[0];#ifdef PMU_MODE_ENABLE  srtrcpyfill(ms2,servName[sid],12);  srtrcpyfill(ms5," ",12);#else  srtrcpyfill(ms5,servName[sid],12);#endif#ifdef PMU_MODE_ENABLE  {  DWORD avgc,avgi;  avgc=pde[sid].c_total/pd->pass;  avgi=pde[sid].i_total/pd->pass;  //cycles/instructions by PMU -------------------------------------------------------  dig_itoa(&ms2[15],pde[sid].c_min,0,6,0,0);ms2[15+6]='/';  dig_itoa(&ms2[31],pde[sid].c_max,0,6,0,0);ms2[31+6]='/';  dig_itoa(&ms2[22],pde[sid].i_min,0,6,0,0);ms2[22+6]=' ';  dig_itoa(&ms2[38],pde[sid].i_max,0,6,0,0);ms2[38+6]=' ';  //time by PMU ----------------------------------------------------------------------  dig_itoa(&ms2[48],pde[sid].c_min*1000/cpuclk,0,2,3,0);ms2[48+6]=' ';  dig_itoa(&ms2[57],pde[sid].c_max*1000/cpuclk,0,2,3,0);ms2[57+6]=' ';  dig_itoa(&ms2[66],pde[sid].c_total*1000/cpuclk/pd->pass,0,2,3,0);ms2[66+6]=' ';  //cpi by PMU -----------------------------------------------------------------------  dig_itoa(&ms2[74],avgc*1000/avgi,0,2,3,0);ms2[74+6]=' ';  RTT_WriteStringEx(ms2);  }#endif#ifdef TRM_MODE_ENABLE  //time by PMU ----------------------------------------------------------------------  dig_itoa(&ms5[48],pde[sid].t_min*1000/trmclk,0,2,3,0);ms5[48+6]=' ';  dig_itoa(&ms5[57],pde[sid].t_max*1000/trmclk,0,2,3,0);ms5[57+6]=' ';  dig_itoa(&ms5[66],pde[sid].t_total*1000/trmclk/pd->pass,0,2,3,0);ms5[66+6]=' ';  RTT_WriteStringEx(ms5);#endif#ifdef PMU_METRIC_ENABLE  //PMU metrics  dig_itoa(&ms3[18],pde[sid].pmu_M1_total/pd->pass,0,6,0,0);ms3[18+6]=' ';  dig_itoa(&ms3[34],pde[sid].pmu_M2_total/pd->pass,0,6,0,0);ms3[34+6]=' ';  RTT_WriteStringEx(ms3);#endif}static void ShowPerfomanceData(BYTE *osname,OS_PERF_DATA *pd){DWORD cpuclk=getCPU_clk()/1000;//DWORD cpuclk=OS_BSP_getCPUClock()/1000;DWORD trmclk=TRM_FREQ_MHZ;dig_itoa(&ms0[8],cpuclk,0,4,0,0);ms0[8+4]='M';RTT_WriteStringEx("\r\nPerfomance info ");RTT_WriteStringEx(osname);RTT_WriteStringEx(ms0);#ifdef PMU_MODE_ENABLE  RTT_WriteStringEx(ms1);#else  RTT_WriteStringEx(ms4);#endif#ifdef PERFTEST_QUEUE  ShowPerfomanceDataHelper(pd,PERF_QU_SID,cpuclk,trmclk);#endif#ifdef PERFTEST_EVF  ShowPerfomanceDataHelper(pd,PERF_EVF_SID,cpuclk,trmclk);#endif#ifdef PERFTEST_SEM  ShowPerfomanceDataHelper(pd,PERF_SEM_SID,cpuclk,trmclk);#endif#ifdef PERFTEST_MTX  ShowPerfomanceDataHelper(pd,PERF_MTX_SID,cpuclk,trmclk);#endif}static void perf_test_prepare(DWORD sid){#ifdef PMU_MODE_ENABLE  #ifdef PMU_METRIC_ENABLE    cnt1.pmuM1[sid]=OS_perfTimerGetCnt1();cnt2.pmuM1[sid]=cnt1.pmuM1[sid];    cnt1.pmuM2[sid]=OS_perfTimerGetCnt2();cnt1.pmuM2[sid]=cnt1.pmuM2[sid];  #endif  cnt1.cyc[sid]=OS_perfTimerGetCycle();cnt2.cyc[sid]=cnt1.cyc[sid];  cnt1.instr[sid]=OS_perfTimerGetCnt0();cnt2.instr[sid]=cnt1.instr[sid];#endif#ifdef TRM_MODE_ENABLE  cnt1.tt[sid]=OS_perfA7TRM_ticks();cnt2.tt[sid]=cnt1.tt[sid];#endif}static void perf_test_wrResult(DWORD sid){#ifdef PMU_MODE_ENABLE  {  DWORD t;  t=OS_perfTime(cnt1.cyc[sid],cnt2.cyc[sid]);  if(t<OSPerfData.pdEntry[sid].c_min)OSPerfData.pdEntry[sid].c_min=t;  if(t>OSPerfData.pdEntry[sid].c_max)OSPerfData.pdEntry[sid].c_max=t;  OSPerfData.pdEntry[sid].c_total+=t;  t=OS_perfTime(cnt1.instr[sid],cnt2.instr[sid]);  if(t<OSPerfData.pdEntry[sid].i_min)OSPerfData.pdEntry[sid].i_min=t;  if(t>OSPerfData.pdEntry[sid].i_max)OSPerfData.pdEntry[sid].i_max=t;  OSPerfData.pdEntry[sid].i_total+=t;  #ifdef PMU_METRIC_ENABLE    t=OS_perfTime(cnt1.pmuM1[sid],cnt2.pmuM1[sid]);    OSPerfData.pdEntry[sid].pmu_M1_total+=t;    t=OS_perfTime(cnt1.pmuM2[sid],cnt2.pmuM2[sid]);    OSPerfData.pdEntry[sid].pmu_M2_total+=t;  #endif  }#endif#ifdef TRM_MODE_ENABLE  {  QWORD t4;  t4=OS_perfTime4(cnt1.tt[sid],cnt2.tt[sid]);  if(t4<OSPerfData.pdEntry[sid].t_min)OSPerfData.pdEntry[sid].t_min=t4;  if(t4>OSPerfData.pdEntry[sid].t_max)OSPerfData.pdEntry[sid].t_max=t4;  OSPerfData.pdEntry[sid].t_total+=t4;  }#endif}static DWORD pass_error=0;OS_TASK_FUNC Test1Entry(OS_TASK_PARM parm){//main test taskDWORD rc,qulen,semv;OS_EVENTFLAGS_VALUE_T evf,evf2,evf3;#ifdef PERFTEST_QUEUE  DWORD qd=0xDEADBEEF;#endifRTT_WriteStringEx("PerfomanceTest task1 started! ----\r\n");OS_task_sleep(500);clearPerfData();for(DWORD i=0;i<PERF_SID_TOTAL;i++){errcnt[i]=0;}OS_perfTimerInit();OS_perfTimerStart();OS_mutex_get(&TEST1Mtx,100);#ifdef ONE_2TH_TASK  OS_task_resume(&TEST2Thread);#endiffor(;;)  {  if(OSPerfData.pass==0)    {    RTT_WriteStringEx("\r\n\r\nPerfomance test started. Wait..\r\n");    sid1=0xFFFF;    OS_task_sleep(200);    }  OS_task_sleep(1);#ifdef PERFTEST_QUEUE  //queue  sid1=PERF_QU_SID;  perf_test_prepare(sid1);  OS_queue_send(&TEST1Queue,&qd,100);  rc=OS_evf_get(&TEST1SrvEvf,QU_MEASURE,OS_EVF_OR,OS_EVF_NO_CLEAR,&evf,OS_WAIT_FOREVER);  if(evf&QU_MEASURE)    {perf_test_wrResult(sid1);}  else    {errcnt[sid1]++;}#endif#ifdef PERFTEST_EVF  //evf  sid1=PERF_EVF_SID;  perf_test_prepare(sid1);  OS_evf_set(&TEST1Evf,EVF_PERFTEST);  rc=OS_evf_get(&TEST1SrvEvf,EVF_MEASURE,OS_EVF_OR,OS_EVF_NO_CLEAR,&evf,OS_WAIT_FOREVER);  if(evf&EVF_MEASURE)    {perf_test_wrResult(sid1);}  else    {errcnt[sid1]++;}#endif#ifdef PERFTEST_SEM  //sem  sid1=PERF_SEM_SID;  perf_test_prepare(sid1);  OS_sem_put(&TEST1Sem);  rc=OS_evf_get(&TEST1SrvEvf,SEM_MEASURE,OS_EVF_OR,OS_EVF_NO_CLEAR,&evf,OS_WAIT_FOREVER);  if(evf&SEM_MEASURE)    {perf_test_wrResult(sid1);}  else    {errcnt[sid1]++;}#endif#ifdef PERFTEST_MTX  //mtx  sid1=PERF_MTX_SID;  perf_test_prepare(sid1);  OS_mutex_put(&TEST1Mtx);  rc=OS_evf_get(&TEST1SrvEvf,MTX_MEASURE,OS_EVF_OR,OS_EVF_NO_CLEAR,&evf,OS_WAIT_FOREVER);  if(evf&MTX_MEASURE)    {perf_test_wrResult(sid1);}  else    {errcnt[sid1]++;}  OS_mutex_get(&TEST1Mtx,OS_WAIT_FOREVER);#endif  //Їа®ўҐаЄ  б®бв®п­Ёп § ¤ з Ї®б«Ґ Їа®е®¤  ----  OS_task_sleep(1);  OS_evf_poll(&TEST1SrvEvf,(QU_MEASURE|EVF_MEASURE|SEM_MEASURE|MTX_MEASURE),&evf2);  OS_evf_poll(&TEST1Evf,EVF_PERFTEST,&evf3);  semv=OS_sem_getValue(&TEST1Sem);  qulen=OS_queue_getlen(&TEST1Queue);  if(evf2!=(QU_MEASURE|EVF_MEASURE|SEM_MEASURE|MTX_MEASURE) ||     evf3!=0 || semv!=0 || qulen!=0)    {    pass_error++;    }  OS_evf_clr(&TEST1SrvEvf,(QU_MEASURE|EVF_MEASURE|SEM_MEASURE|MTX_MEASURE));  //-------------------------------------------  OSPerfData.pass++;  if(OSPerfData.pass>=TEST_PASS_NUM)    {    ShowPerfomanceData("threadX",&OSPerfData);    OS_task_sleep(2000);    clearPerfData();    OSPerfData.pass=0;    }  }}static void perf_test_fixCnt(DWORD sid){#ifdef PMU_MODE_ENABLE  cnt2.cyc[sid]=OS_perfTimerGetCycle();  cnt2.instr[sid]=OS_perfTimerGetCnt0();  #ifdef PMU_METRIC_ENABLE    cnt2.pmuM1[sid]=OS_perfTimerGetCnt1();    cnt2.pmuM2[sid]=OS_perfTimerGetCnt2();  #endif#endif#ifdef TRM_MODE_ENABLE  cnt2.tt[sid]=OS_perfA7TRM_ticks();#endif}OS_TASK_FUNC Test2Entry(OS_TASK_PARM parm){DWORD qd;OS_EVENTFLAGS_VALUE_T evf;RTT_WriteStringEx("PerfomanceTest task2 started! ----\r\n");for(;;)  {  #ifdef PERFTEST_QUEUE    OS_queue_receive(&TEST1Queue,&qd,OS_WAIT_FOREVER);    perf_test_fixCnt(PERF_QU_SID);    OS_evf_set(&TEST1SrvEvf,QU_MEASURE);  #endif  #ifdef PERFTEST_EVF    sid2=PERF_EVF_SID;    OS_evf_get(&TEST1Evf,EVF_PERFTEST,OS_EVF_OR,OS_EVF_NO_CLEAR,&evf,OS_WAIT_FOREVER);    perf_test_fixCnt(PERF_EVF_SID);    OS_evf_clr(&TEST1Evf,EVF_PERFTEST);    OS_evf_set(&TEST1SrvEvf,EVF_MEASURE);  #endif  #ifdef PERFTEST_SEM    sid2=PERF_SEM_SID;    OS_sem_get(&TEST1Sem,OS_WAIT_FOREVER);    perf_test_fixCnt(PERF_SEM_SID);    OS_evf_set(&TEST1SrvEvf,SEM_MEASURE);  #endif  #ifdef PERFTEST_MTX    sid2=PERF_MTX_SID;    OS_mutex_get(&TEST1Mtx,OS_WAIT_FOREVER);    perf_test_fixCnt(PERF_MTX_SID);    OS_mutex_put(&TEST1Mtx);    OS_evf_set(&TEST1SrvEvf,MTX_MEASURE);  #endif  }}OS_TASK_FUNC Test3Entry(OS_TASK_PARM parm){RTT_WriteStringEx("PerfomanceTest task3 started! ----\r\n");for(;;)  {  //volatile DWORD n;  //for(n=0;n<100000000;n++){/*empty*/}  OS_task_sleep(2000);  //RTT_WriteStringEx("task3 loop..\r\n");  }}void TEST1_DevInit(void){sid1=0xDEAD;sid2=0xBEEF;OS_evf_create(&TEST1SrvEvf,"evf_SrvTest1");OS_evf_create(&TEST1Evf,"evf_Test1");OS_mutex_create(&TEST1Mtx,"mtx_Test1");OS_sem_create(&TEST1Sem,"sem_Test1",10,0);OS_queue_create(&TEST1Queue,"qu_TEST1",TEST1_QUEUE_MSGSIZE,                (OS_QUEUE_DATABUF_T)TEST1QueueData,TEST1_QUEUE_LEN);OS_task_create(&TEST1Thread,"th_TEST1",Test1Entry,               (OS_TASK_PARM)0, //start thread parm value               TEST1ThreadStack,TEST1_STACK_SIZE,               TEST1_PRIORITY,TEST1_PRIORITY,OS_NO_TIMESLICE,OS_TH_AUTOSTART);OS_task_create(&TEST2Thread,"th_TEST2",Test2Entry,               (OS_TASK_PARM)0, //start thread parm value               TEST2ThreadStack,TEST2_STACK_SIZE,               TEST2_PRIORITY,TEST2_PRIORITY,OS_NO_TIMESLICE,OS_TH_DONTSTART);#ifdef MPCORE_MODE  OS_task_create(&TEST3Thread,"th_TEST3",Test3Entry,                 (OS_TASK_PARM)0, //start thread parm value                 TEST3ThreadStack,TEST3_STACK_SIZE,                 TEST3_PRIORITY,TEST3_PRIORITY,OS_NO_TIMESLICE,OS_TH_AUTOSTART);  OS_task_smp_core_exclude(&TEST1Thread,AFF_MASK_CORE0_ONLY);  #ifndef DIFFERENT_CORES    #define TH2_AFFINITY_MASK     AFF_MASK_CORE0_ONLY  #else    #define TH2_AFFINITY_MASK     AFF_MASK_CORE1_ONLY  #endif  OS_task_smp_core_exclude(&TEST2Thread,TH2_AFFINITY_MASK);  OS_task_smp_core_exclude(&TEST3Thread,TH2_AFFINITY_MASK);#endif}

os_perf_timer_pmu.c:

#include "..\h\typedef.h"#include "DBGUTIL\pmu\sys_pmu.h"#include "os_perf_timer.h"extern DWORD OS_BSP_getPMU0Address(void);extern DWORD getCPUID(void);static DWORD pmuInited=0;void OS_perfTimerInit(void){_pmuInit_();_pmuSetCountEvent_(0,PMU_INST_ARCH_EXECUTED);_pmuSetCountEvent_(1,0x10); //_pmuSetCountEvent_(2,0x12); //0x68pmuInited=1;}void OS_perfTimerStop(void){_pmuStopCounters_(pmuCYCLE_COUNTER|pmuCOUNTER0|pmuCOUNTER1|pmuCOUNTER2);}DWORD OS_perfTimerGetCycle(void){return(_pmuGetCycleCount_());}DWORD OS_perfTimerGetCnt0(void){return(_pmuGetEventCount_(0));}DWORD OS_perfTimerGetCnt1(void){return(_pmuGetEventCount_(1));}DWORD OS_perfTimerGetCnt2(void){return(_pmuGetEventCount_(2));}void OS_perfTimerStart(void){if(!pmuInited){_pmuInit_();}_pmuResetCounters_();_pmuStartCounters_(pmuCYCLE_COUNTER|pmuCOUNTER0|pmuCOUNTER1|pmuCOUNTER2);}DWORD pmuerr=0;DWORD OS_perfTime(DWORD t1,DWORD t2){DWORD t;if(t2>=t1)  {t=t2-t1;}else  {t=t2+(0xFFFFFFFF-t1);}return(t);}DWORD OS_perfTime4(QWORD t1,QWORD t2){DWORD t;if(t2>=t1)  {t=t2-t1;}else  {t=t2+(0xFFFFFFFFFFFFFFFF-t1);}return(t);}QWORD OS_perfA7TRM_ticks(void){DWORD low,high;asm("MRRC p15, 0, %0, %1, c14" : "=r"(low), "=r"(high));return(((QWORD)high<<32)|low);}

Здесь используются врапперы OSAL (OS Abstraction Layer), написанные много лет назад для безболезненной замены rtos в проекте. Тот проект проделал путь threadx->freertos->embos по определенным причинам, не относящимся к теме данной статьи.

osal.h:

#include "..\h\typedef.h"//select one from list ---#ifdef USE_EMBOS  #define PRIO_LVL 255-#else  #ifdef USE_FREERTOS    #define PRIO_LVL 31-  #else    #ifdef USE_THREADX      #define PRIO_LVL 0+    #endif  #endif#endif#ifdef USE_EMBOS  #include "embOS_AL.h"#else  #ifdef USE_FREERTOS    #include "freeRTOS_AL.h"  #else    #ifdef USE_THREADX      #include "threadX_AL.h"    #endif  #endif#endif

А теперь плавно переходим к результатам. Тесты проведены на Allwinner T113, включены кэши L1-L2 и MMU.

одноядерная версия threadX:

Clk: CPU=1008000KHz   DRAM=0792000KHz  AXI=504000KHz  PERIPH=600000KHz     AHB=200000KHz    APB0=100000KHz     APB1=024000KHzAverage: 64 passesT133 Uni DRAM ===================================================Perfomance info threadX CPUclk=1008MHzservice_name   min[cyc/inst]   max[cyc/inst]    min[uS]  max[uS]  avg[uS] cpiqueue          000524/000310   000972/000310    00.519   00.964   00.564  01.832               Cortex-A7 TRM result             00.541   00.958   00.579PMU metrics    c1=000009       c2=000030eventFlag      000516/000315   000635/000315    00.511   00.629   00.527  01.685               Cortex-A7 TRM result             00.500   00.666   00.549PMU metrics    c1=000006       c2=000027semaphore      000453/000264   000666/000264    00.449   00.660   00.469  01.791               Cortex-A7 TRM result             00.458   00.666   00.475PMU metrics    c1=000005       c2=000023mutex          000496/000300   000672/000300    00.492   00.666   00.511  01.716               Cortex-A7 TRM result             00.500   00.666   00.550PMU metrics    c1=000004       c2=000031

а вот замер только при помощи таймера:

T133 Uni DRAM ===================================================Perfomance info threadX CPUclk=1008MHzservice_name                                    min[uS]  max[uS]  avg[uS]queue          Cortex-A7 TRM result             00.500   00.916   00.541eventFlag      Cortex-A7 TRM result             00.500   00.708   00.553semaphore      Cortex-A7 TRM result             00.500   00.666   00.516mutex          Cortex-A7 TRM result             00.458   00.583   00.473

SMP версия threadX, тестовые задачи запущены на одном ядре, второе простаивает:

Clk: CPU=1008000KHz   DRAM=0936000KHz  AXI=504000KHz  PERIPH=600000KHz     AHB=200000KHz    APB0=100000KHz     APB1=024000KHzAverage: 64 passesT133 Smp DRAM =======================================Perfomance info threadX CPUclk=1008MHzservice_name   min[cyc/inst]   max[cyc/inst]    min[uS]  max[uS]  avg[uS] cpiqueue          002029/000759   002996/000759    02.012   02.972   02.120  02.815               Cortex-A7 TRM result             02.000   02.958   02.109PMU metrics    c1=000048       c2=000092eventFlag      002424/000845   002610/000845    02.404   02.589   02.470  02.946               Cortex-A7 TRM result             02.416   02.625   02.481PMU metrics    c1=000044       c2=000100semaphore      002252/000770   002487/000770    02.234   02.467   02.311  03.024               Cortex-A7 TRM result             02.250   02.500   02.322PMU metrics    c1=000039       c2=000092mutex          002417/000862   002780/000862    02.397   02.757   02.445  02.859               Cortex-A7 TRM result             02.416   02.791   02.457PMU metrics    c1=000035       c2=000103

замер таймером:

T133 Smp DRAM =======================================Perfomance info threadX CPUclk=1008MHzservice_name                                    min[uS]  max[uS]  avg[uS]queue          Cortex-A7 TRM result             01.958   02.958   02.060eventFlag      Cortex-A7 TRM result             02.375   02.541   02.428semaphore      Cortex-A7 TRM result             02.250   02.458   02.303mutex          Cortex-A7 TRM result             02.375   02.791   02.445

SMP версия threadX, тестовые задачи запущены на разных ядрах, замер возможен только таймером:

T133 Smp DRAM  =======================================Perfomance info threadX CPUclk=1008MHzservice_name                                    min[uS]  max[uS]  avg[uS]queue          Cortex-A7 TRM result             00.791   01.041   00.818eventFlag      Cortex-A7 TRM result             00.666   01.958   01.063semaphore      Cortex-A7 TRM result             00.500   01.708   00.898mutex          Cortex-A7 TRM result             00.750   02.083   01.509

Анализируем результаты (smp версия запускалась при чуть иной инициализации памяти DRAM, но на результатах это слабо сказывалось).

Переход на SMP версию rtos сопровождается заметным усложнением служебного кода — число инструкций увеличивается в 2-2.5, кроме того увеличивается CPI (cycles per instruction), что косвенно свидетельствует об увеличении числа ветвлений в коде и, соответственно, увеличении числа промахов предсказателя ветвлений. В этом режиме наблюдается ухудшение времени реакции rtos примерно в 4 раза.

Однако, разнесение задач по разным ядрам уменьшает эти штрафы времени! Вероятно, поскольку код делится на разные ядра. Итоговый результат — ухудшение относительно одно процессорной версии в 1.5-2 раза. Считаю вполне хороший результат.

И небольшое дополнение. Раз уж тестик обновлен, обновил результаты сравнения разных rtos.
Это сравнение запущено на плате с одноядерным Allwinner V3s.

threadX 6.1:

Perfomance info  CPUclk=0900MHzservice_name   min[cyc/inst]   max[cyc/inst]    min[uS]  max[uS]  avg[uS] cpiqueue          000497/000302   000597/000302    00.552   00.663   00.595  01.771               Cortex-A7 TRM result             00.583   00.708   00.613PMU metrics    c1=000011       c2=000039eventFlag      000575/000332   000636/000332    00.638   00.706   00.665  01.801               Cortex-A7 TRM result             00.666   00.750   00.689PMU metrics    c1=000010       c2=000041semaphore      000488/000278   000578/000278    00.542   00.642   00.575  01.859               Cortex-A7 TRM result             00.541   00.666   00.582PMU metrics    c1=000009       c2=000033mutex          000523/000333   000589/000333    00.581   00.654   00.607  01.639               Cortex-A7 TRM result             00.583   00.666   00.626PMU metrics    c1=000008       c2=000045

embos 4.38:

Perfomance info  CPUclk=0900MHzservice_name   min[cyc/inst]   max[cyc/inst]    min[uS]  max[uS]  avg[uS] cpiqueue          001218/000524   001315/000524    01.353   01.461   01.401  02.406               Cortex-A7 TRM result             01.375   01.500   01.425PMU metrics    c1=000018       c2=000075eventFlag      000820/000416   000937/000416    00.911   01.041   00.962  02.081               Cortex-A7 TRM result             00.916   01.083   00.984PMU metrics    c1=000013       c2=000062semaphore      000837/000417   001003/000417    00.930   01.114   00.958  02.067               Cortex-A7 TRM result             00.958   01.166   00.981PMU metrics    c1=000015       c2=000062mutex          001339/000704   001711/000704    01.487   01.901   01.544  01.973               Cortex-A7 TRM result             01.500   01.916   01.561PMU metrics    c1=000031       c2=000112

Здесь мы видим, что качество кода threadX несколько повыше. В нем используется довольно много асм вставок, полагаю, для оптимизации траектории исполнения кода в случае наиболее вероятных ветвлений, что отражено в лучшей цифре CPI.

В общем, результат нынешнего теста согласуется с прогоном аналогичного теста в 2017, когда происходил переход threadx->freertos->embos. Точных цифр под рукой нет, надо искать где-то тот файлик, но на память такое соотношение (2-3) и было. А freertos тогдашняя (9.0.0) отставала от эти обеих ну просто ОЧЕНЬ сильно. Сейчас заниматься запуском freertos на этих платах совершенно неинтересно.

ссылка на оригинал статьи https://habr.com/ru/articles/1063284/