一、前言
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/