【原创】linux实时操作系统xenomai x86平台基准测试(benchmark)

一、前言

benchmark 即基准测试。通常操作系统主要服务于应用程序,其运行也是需要一定cpu资源的,一般来说操作系统提供服务一定要快,否则会影响应用程序的运行效率,尤其是实时操作系统。所以本文针对操作系统来做一些基准测试,看看在低端x86平台上,xenomai提供我们平时常用的服务所需要的时间,清楚地了解该平台上一些xenomai服务的消耗,有时能有利于我们进一步优化程序。影响因素有:主机CPU的结构、指令集以及CPU特性、运算速度等。

目前大多商业实时操作系统会提供详细benchmark测试,比如VxWorks,目前xenomai没有这类的方式,所以借鉴VxWorks的测试方式,对xenomai进行同样测试,所以文章中的测试项命名可能在Linux开发人员看来有点别扭,切勿见怪,其中一些具体流程可见本博客另外一篇文章xenomai与VxWorks实时性对比(资源抢占上下文切换对比)

测试环境:

CPU:Intel j1900

内存:4GB DDR3

注:测试数据仅供个人参考,单位us,每项测试次数500万次,编写测试用例使用的接口为Alchemy API,原因主要是Alchemy API比较好编写。

二、 测试数据处理

对于每个基准测试,通过在操作前读取时间戳\(t1\),该操作完成后读取时间戳\(t2\)\(t2\)\(t1\)之间的差值就是测该操作的耗时。

1.1 测试注意事项

需要注意的是,由于我们是基准测试,所以\(t1\)~\(t2\)这段时间尽量不要被不相关的事务打断,比如处理不相关的中断、非测试范围内的任务抢占等。为此需要考虑如下。

① 执行测试操作的任务优先级必须最高,两个任务间交互的测试类似。

② 必须检测t1-t2之间的非相关中断,并丢弃对应的测试数据,由于我们已将非xenomai的中断隔离到其cpu0,且无其他实时设备中断,除各种异常外,剩下与xenomai相关的就是定时器中断了,所以仅对tick中断处理,如果测试过程中产生了定时器中断,则忽略这组数据,因此需要为xenomai添加一个系统调用来获取中断信息,测试前后通过该系统调用获中断信息,以此判断测试的过程中有没有中断产生。

③ 读取时间戳的操作也是需要执行时间的,所以需要从结果中减去该时间的影响,测量读取时间戳的需要的时间很简单,通过连续两次读取时间戳\(a1\),\(a2\),\(a2-a1\)就是函数 _M_TIMESTAMP()的执行需要时间。

1.2 数据的处理

得到无误的操作耗时、测试次数后计算平均值最大值、最小值即可;

1.3 测试结构

根据以上,每个测试的流程及代码结构如下:

① 读取起始tick

② 开始测试循环

③ 读取时间戳a

④ 读取起始时间戳b

被测试的操作

⑥读取结束时间戳c

⑦判断是否是loadrun,是则丢弃本次结果跳转到③

⑧读取tick,判断本次测试是否位于同一tick内,否则丢弃本次结果跳转到③

⑨判读耗时是都正确(a-b且b-c为正值),是则为有效值,否则丢弃本次结果跳转到③

	unsigned long  a;
    unsigned long  b;
    unsigned long  c;
    ULONG	   tick;
    BOOL	   loadRun = TRUE;  /*排除cache对测试的影响,丢弃第一次测试的数据*/

    tick = tickGet();  /*确保测试在同一个tick内完成*/

 	/*循环测试iterations次操作并统计结果*/
    for (counter = 0; counter < pData->iterations; counter++)
	{	
    
	a = _M_TIMESTAMP();
	b = _M_TIMESTAMP();  /*起始时间*/
	
        wd = wdCreate ();/*测试的操作*/

	c = _M_TIMESTAMP();	/*结束时间*/
        
	/*数据统计处理*/
	BM_DATA_RECORD (((c >= b) && (b >= a)), c - b, b - a,
			counter, tick, loadRun);
	}

二、测试项

明白数据统计处理后剩下的就是其中测试的具体操作了,benchmark 分别对二值信号量(semB)、计数信号量(semC)、互斥量(semM)、读写信号量(SemRW)、任务(Task)、消息队列(msgq)、事件(event)、 中断响应(interrupt)、上下文切换(contexswitch)、时钟抖动(TaskJitter、IntJitter)在各种可能的情况下,测试该操作的耗时。

2.1 时间戳

测试读时间戳耗时bmTimestampRead

 	unsigned long a;
    unsigned long b;
    ULONG	  tick;
    BOOL	  loadRun = TRUE;					\

    tick = tickGet();

    for (counter = 0; counter < pData->iterations; counter++)
        {
	a = _M_TIMESTAMP();
	b = _M_TIMESTAMP();

	/* validate and record data */

	BM_DATA_RECORD ((b > a), b - a, 0, counter, tick, loadRun);
	}

min avg max
0.084 0.094 0.132

2.2 任务切换

2.2.1信号量响应上下文切换时间

bmCtxSempend: 同一cpu上,高优先级任务对空信号量P操作阻塞,到低优先任务激活的时间。

bmCtxSemUnpend: 同一cpu上,低优先级任务对信号量V操作到高优先任务激活的时间。

CtxSmpAffinitySemUnPend: 高低优先级任务运行于不同cpu上,高优先级任务对空信号量P操作阻塞,到低优先任务激活的时间。

CtxSmpNoAffinitySemUnPend: 不设置亲和性,随系统调度,低优先级任务对信号量V操作到高优先任务激活的时间。

min avg max
bmCtxSempend 2.136 2.193 2.641
bmCtxSemUnpend 2.351 2.395 2.977
CtxSmpAffinitySemUnPend 0.000 0.752 2.642
CtxSmpNoAffinitySemUnPend 2.389 2.454 2.797
2.2.2消息队列响应上下文切换时间

bmCtxMsgqPend:同一cpu上,高优先级任务对空消息队列接收数据阻塞,到低优先任务激活的时间。

bmCtxMsgqUnpend:同一cpu上, 低优先级任务写消息队列到高优先任务激活的时间。

CtxSmpAffinityMsgQUnPend:高低优先级任务运行于不同cpu上,高优先级任务对空消息队列接收数据阻塞,到低优先任务激活的时间。

CtxSmpNoAffinityMsgQUnPend:不设置亲和性,随系统调度, 低优先级任务写消息队列到高优先任务激活的时间。

min avg max
bmCtxMsgqPend 2.496 2.529 2.833
bmCtxMsgqUnpend 2.882 2.949 3.374
CtxSmpAffinityMsgQUnPend 5.245 5.497 10.589
CtxSmpNoAffinityMsgQUnPend 2.941 2.995 3.636
2.2.3事件响应上下文切换时间

bmCtxMsgqPend:高优先级任务接收事件阻塞,到低优先任务激活的时间。

bmCtxMsgqUnpend: 低优先级任务发送事件到高优先任务激活的时间。

min avg max
bmCtxEventPend - - -
bmCtxEventUnpend - - -
CtxSmpAffinityEventQUnPend - - -
CtxSmpNoAffinityEventUnPend - - -
2.2.2.4任务上下文切换时间

bmCtxTaskSwitch:同一cpu上,优先级调度下的任务切换时间。

min avg max
bmCtxTaskSwitch 0.703 1.633 2.594

2.3 信号量(Semaphore)

1. 信号量的创建与删除

bmSemBCreate: 创建一个信号量耗时。

bmSemBDelete: 删除一个信号量耗时。

min avg max
bmSemCreate 10.433 11.417 12.977
bmSemDelete 10.276 11.431 12.317
2. 信号量PV操作

SemGiveNoTask:当没有任务阻塞在信号量上时,对空信号量V操作消耗的时间。

SemGiveTaskInQ:同一CPU上,高优先级任务阻塞在信号量时,低优先级任务释放信号量操作消耗的时间。

SemTakeUnavail:单任务对不可用的信号量P操作消耗的时间。

SemTakeAvail:单任务对可用信号量非阻塞P操作消耗的时间。

bmSemGiveTake:单任务对同一信号量连续一次PV操作消耗的时间。

min avg max
SemGiveNoTask 0.099 0.110 0.132
SemGiveTaskInQ 1.837 2.036 2.281
SemTakeAvail 0.084 0.094 0.108
SemTakeUnavail 0.111 0.125 0.144
SemGiveTake 0.187 0.192 0.198
SemPrioInv 6.531 6.842 11.968

2.4 互斥量(Mutex)

2.4.1 互斥量的创建与删除

MutexCreate:创建一个互斥量耗时。

MutexDelete:删除一个互斥量耗时。

2.4.2 互斥量PV操作

MutexGiveNoTask:当没有任务阻塞在mutex上时,释放mutex操作消耗的时间。

MutexGiveTaskInQ:同一CPU上,高优先级任务阻塞在mutex时,低优先级任务释放mutex操作消耗的时间。

MutexTakeUnavail:当没有mutex可用时,对mutex请求操作的耗时。

MutexTakeAvail:在mutex可用时,请求mutex消耗的时间。

MutexGiveTake:单任务对mutex连续请求释放消耗的时间。

min avg max
MutexCreate 2.881 2.947 3.205
MutexDelete 2.039 2.084 2.209
MutexGiveNoTask 0.033 0.044 0.066
MutexGiveTaskInQ 0.047 0.117 0.228
MutexTakeAvail 0.084 0.094 0.114
MutexGiveTake 0.118 0.122 0.148

2.5 消息队列(Message Queue)

2.5.1 创建与删除

MsgQCreate:创建一个MsgQ需要的时间。

MsgQDelete:删除一个MsgQ需要的时间。

2.5.2 数据收发

MsgQRecvAvail:当MsgQ内有数据时,接收1Byte数据需要的时间。

MsgQRecvNoAvail:当MsgQ没有数据时,非阻塞接收1Byte数据需要的时间。

MsgQSendPend:高优先级等待数据时,发送1Byte数据需要的时间。

MsgQSendNoPend:没有任务等待数据时,发送1Byte数据需要的时间。

MsgQSendQFull:当MsgQ满时,非阻塞发送1Byte数据需要的时间。

min avg max
MsgQCreate 5.991 6.324 6.855
MsgQDelete 3.733 3.849 4.046
MsgQRecvAvail 0.240 0.279 0.396
MsgQRecvNoAvail 0.216 0.267 0.349
MsgQSendPend 2.401 2.647 3.902
MsgQSendNoPend 1.223 1.262 1.536
MsgQSendQFull 0.228 0.275 0.408

2.6 定时器(Alarm)

AlarmCreate:创建一个alarm的时间。

AlarmDelStarted:删除一个已经激活的alarm的时间。

AlarmDelNotStarted:删除一个未激活alarm的时间。

AlarmStartQEmpty:任务没有alarm时,start一个alarm需要的时间。

AlarmStartQEmpty:任务在已有一个 alarm的基础上,再start一个alarm需要的时间。

AlarmCancel:stop一个alarm需要的时间。

min avg max
AlarmCreate 4.790 4.937 7.719
AlarmDelStarted 3.637 3.804 4.250
AlarmDelNotStarted 3.420 3.523 4.381
AlarmStartQEmpty 1.860 2.079 3.158
AlarmStartQFull 1.835 1.897 2.101
AlarmCancel 1.596 1.680 2.677

2.7 事件(Event)

EventSendSelf: 任务向自己发送一个Event需要的时间。

EventReceiveAvailable: 接收一个已产生的Event需要的时间。

EventReceiveUnavailable: 非阻塞接收一个未产生的Event需要的时间。

EventTaskSendWanted: 高优先级等待Event时,发送Event需要的时间。

EventTaskSendUnwanted: 无任务等待Event时,发送Event需要的时间。

min avg max
EventSendSelf 4.790 4.937 7.719
EventReceiveAvailable 3.637 3.804 4.250
EventReceiveUnavailable 3.420 3.523 4.381
EventTaskSendWanted 1.860 2.079 3.158
EventTaskSendUnwanted 1.835 1.897 2.101

2.8 任务(Task)

2.8.1 任务创建激活

TaskSpawn: 创建并激活一个任务需要的时间。

TaskDelete:删除一个任务需要的时间。

TaskInit:创建一个任务需要的时间。

TaskActivate:激活新创建的任务需要的时间。

2.8.2 任务调度控制

TaskSuspendReady:对一个已经处于ready状态的任务suspend操作需要的时间。

TaskSuspendPend:对一个等待资源处于pend状态的任务进行suspend操作需要的时间。

TaskSuspendSusp:对刚创建的处于Suspend任务 执行Suspend操作需要的时间。

TaskSuspendDelay:对一个处于sleep任务进行suspend操作需要的时间。

TaskResumeReady:对一个处于Ready状态的任务进行Resume操作需要的时间。

TaskResumePend:对一个等待资源处于pend状态的任务进行Resume操作需要的时间。

TaskResumeSusp:对一个处于Suspend状态的任务进行Resume操作需要的时间。

TaskResumeDelay:对一个处于sleep任务进行Resume操作需要的时间。

TaskPrioritySetReady:对一个处于Ready状态任务修改优先级操作需要的时间。

TaskPrioritySetPend:对一个处于pend状态任务修改优先级操作需要的时间。

bmTaskCpuAffinityGet:获取任务的亲和性需要的时间。

bmTaskCpuAffinitySet:设置任务的亲和性需要的时间。

min avg max
TaskSpawn(1000万次) 150.649 153.859 1162.041
TaskDelete(1000万次) 136.074 145.766 189.952
TaskInit(1000万次) 178.703 185.015 436.639
TaskActivate 1.052 1.336 2.986
TaskSuspendReady 1.404 1.444 1.681
TaskSuspendPend 0.035 1.392 1.561
TaskSuspendSusp 0.151 0.155 0.321
TaskSuspendDelay 1.356 1.401 1.525
TaskResumeReady 0.146 0.155 0.487
TaskResumePend 0.756 0.802 0.877
TaskResumeSusp 0.204 0.248 0.324
TaskResumeDelay 0.180 0.228 0.300
TaskPrioritySetReady 18.925 21.002 21.855
TaskPrioritySetPend 19.046 21.014 28.296
TaskCpuAffinityGet - - -
TaskCpuAffinitySet 8.332 9.541 19.808

Cyclic:如下操作的流程循环一次的耗时,图中M表示mutex,B表示Semaphore。

/*
       Higher Priority 	   Lower Priority
		 Task1			   			Task2
		=============== 		   ==============


	   semTake(M)
	   semGive(M)
		 |
		 V
	   semGive(B)
	   semTake(B)
		 |
		 V
	   semTake(B)
		 \
		  \
		   \------------->	  semTake(M)
							  semGive(B)
									  /
									 /
	   semTake(M)	  <-------------/
		 \
		  \
		   \------------->	  semGive(M)
									  /
									 /
	   semGive(M)	  <-------------/
		 |
		 V
	   taskSuspend()  <-------------/
		 \
		  \
		   \------------->	  taskResume()
								      /
									 /
	   msgQSend()	  <-------------/
	   msgQReceive()
		 |
		 V
	   msgQReceive()
		 \
		  \
		   \------------->	  msgQSend()
									  /
									 /
	   taskDelay(0)   <-------------/
		 |
		 V
	   eventReceive()
		 \
		  \
		   \------------->	  eventSend()
									  /
									 /
	   repeat...	  <-------------/
*/
min avg max
Cyclic 33.589 34.409 36.471

版权声明:本文为本文为博主原创文章,转载请注明出处。如有问题,欢迎指正。博客地址:https://www.cnblogs.com/wsg1100/

posted @ 2020-10-25 21:39  沐多  阅读(149)  评论(0编辑  收藏  举报