продолжение темы 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/