1. 內存性能測試需求
SylixOS作爲大型實時操作系統,在各個領域當中都會有重要應用。其中,在高性能運算領域需要較高的內存讀寫性能,因此我們需要有一個直觀,跨平臺的內存測試軟件來給出SylixOS在各個平臺的內存性能數據。
2. 內存性能測試原理
內存性能無非內存訪問的讀寫速度,內存讀寫速度的測試需要在全速的內存的操作前後記錄操作耗時,根據操作耗時和操作的內存大小得出操作的速度。原理如公式公式 2 1所示,V爲內存操作速度,S爲內存操作的容量,T爲內存操作的耗時。
公式 2-1 速度計算公式
3. 內存性能測試程序設計與實現
由於考慮到需要對比其他操作系統,儘量採用通用的內存操作接口來實現相關程序。測試程序流程圖如所示。
圖 31 內存測試程序流程圖
3.1 內存操作
在內存操作方面,主要考慮到內存的讀,寫這兩種操作。
3.1.1 讀內存
關於讀操作,可以直接操作內存指針,將內存對應位置的值直接賦值給寄存器,這樣可以達到較高的操作效率,另外由於操作過程中的循環可能導致耗時過多,需要做一些優化。
3.1.2 寫內存
關於寫操作,可以直接採用memset函數將內存空間內所有的位置全部寫0。
3.1.3 內存拷貝
關於內存拷貝,可以使用memcpy函數來測試下內存綜合拷貝的效率。
3.2 計時
在讀寫測試中,對時間的精確記錄是非常重要的。在常規的POSIX平臺下,gettimeofday是個精確到微秒級別的時間獲取函數,在SylixOS下,gettimeofday調用到系統的高精度時鐘,所以也是十分精準的,因此我們可以在計時的操作中使用gettimeofday函數。
3.3 結果輸出
由於內存操作是十分快速的,並且常規的數據輸出是相對耗時的,所以需要優先記錄好內存操作的容量和操作耗時,在最後全部處理完時在進行運算輸出測試結果。
3.4 具體實現
3.4.1 程序優先級的調整
程序運行時需要將優先級調整到相對不會被打斷的優先級,這裏配置爲100,具體程序如程序清單 31所示。
程序清單 31
struct sched_param schedParam;int iRet; iRet = sched_getparam(0, &schedParam); /*獲取進程控制塊信息 */if (iRet != 0) { perror("sched_getparam"); return -1; } /*設置優先級爲100 */schedParam.sched_priority = PX_PRIORITY_CONVERT(100); iRet = sched_setscheduler(0, SCHED_RR, &schedParam);if (iRet != 0) { return -1; }
3.4.2 內存空間申請並初始化
用於內存測試的內存空間必須是連續的物理空間,這樣才能得到比較精準的測試結果,這裏採用的是calloc函數來分配內存空間,具體函數實現如程序清單 3-2所示。
程序清單 3-2
/********************************************************************************** ** 函數名稱: makeSpace ** 功能描述: 申請內存空間 ** 輸 入: ullSpaceSize 要申請空間的大小 ** 輸 出: 申請到的內存空間指針 ** 調用模塊: NONE**********************************************************************************/static long *pMakeSpace (unsigned long long ullSpaceSize){ unsigned int uiLen = sizeof(long); unsigned long long i; long *plSpace = NULL; plSpace = calloc(ullSpaceSize, uiLen); /* 申請連續的內存空間 */ if (plSpace == NULL) { /* 申請失敗直接返回退出 */ perror("allocating memory failed!"); exit(1); } for (i = 0; i < ullSpaceSize; i++) { /* 內存內容賦初值 */ plSpace[i] = 0xaa; } return plSpace; /* 返回申請的內存空間指針 */}
3.4.3 寫測試函數
寫測試函數主要是使用memset函數將連續的空間寫0,多次操作來獲取操作的時間。具體實現如程序清單 33所示。
程序清單 3-3
/********************************************************************************** ** 函數名稱: writebenchMark ** 功能描述: 內存寫性能處理 ** 輸 入: ullSpaceSize 要申請空間的大小 ** lSpaceDest 目標內存空間 ** 輸 出: 申請到的內存空間指針 ** 調用模塊: NONE**********************************************************************************/static double writebenchMark (unsigned long long ullSpaceSize, long *plSpaceDest){ struct timeval tmStart; struct timeval tmEnd; unsigned int uiLen = sizeof(long); unsigned long long ullSpaceBytes = ullSpaceSize * uiLen; int i; double dRet = 0; gettimeofday(&tmStart, NULL); /* 獲取拷貝前的系統時間 */ for (i = 0; i < CPLOOPS; i++) { memset(plSpaceDest, 0, ullSpaceBytes); /* 做循環拷貝 */ } gettimeofday(&tmEnd, NULL); /* 獲取拷貝完成後的系統時間 */ dRet = ((double)(tmEnd.tv_sec * 1000000 - /* 獲取前後耗時時間 */ tmStart.tv_sec * 1000000 + tmEnd.tv_usec - tmStart.tv_usec))/1000000; return dRet; /* 返回耗時 */}
3.4.4 讀測試函數
讀測試主要直接將內存數據全部複製到寄存器中的操作來測試的,其中由於循環的次數較多也會影響結果,顧做了一些調整,具體實現如程序清單 3-4所示。
程序清單 3-4
/********************************************************************************** ** 函數名稱: readbenchMark ** 功能描述: 內存讀性能處理 ** 輸 入: ullSpaceSize 要申請空間的大小 ** lSpaceSrc 源內存空間 ** 輸 出: 申請到的內存空間指針 ** 調用模塊: NONE **********************************************************************************/static double readbenchMark (unsigned long long ullSpaceSize, long *plSpaceSrc){ struct timeval tmStart; struct timeval tmEnd; unsigned long long ullCouont = 0; int i; double dRet = 0; register long lReader = 0; long *plSpaceSrcTmp = NULL; dRet = lReader; gettimeofday(&tmStart, NULL); /* 獲取拷貝前的系統時間 */ for (i = 0; i < CPLOOPS; i++) { plSpaceSrcTmp = plSpaceSrc; for (ullCouont = 0; ullCouont < ullSpaceSize; ullCouont +=64 ) { lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; lReader = *plSpaceSrcTmp++; } } gettimeofday(&tmEnd, NULL); /* 獲取拷貝完成後的系統時間 */ dRet = ((double)(tmEnd.tv_sec * 1000000 - /* 獲取前後耗時時間 */ tmStart.tv_sec * 1000000 + tmEnd.tv_usec - tmStart.tv_usec))/1000000; return dRet; /* 返回耗時 */}
3.4.5 拷貝測試函數
拷貝測試函數主要測試memcpy函數拷貝兩個內存數據的效率。具體實現如程序清單 35所示
程序清單 35
/********************************************************************************** ** 函數名稱: cpbenchMark ** 功能描述: 內存拷貝性能處理 ** 輸 入: ullSpaceSize 要申請空間的大小 ** lSpaceSrc ** lSpaceDest ** 輸 出: 申請到的內存空間指針 ** 調用模塊: NONE**********************************************************************************/static double cpbenchMark (unsigned long long ullSpaceSize, long *plSpaceSrc, long *plSpaceDest){ struct timeval tmStart; struct timeval tmEnd; unsigned int uiLen = sizeof(long); unsigned long long ullSpaceBytes = ullSpaceSize * uiLen; int i; double dRet = 0; gettimeofday(&tmStart, NULL); /* 獲取拷貝前的系統時間 */ for (i = 0; i < CPLOOPS; i++) { mempcpy(plSpaceDest, plSpaceSrc, ullSpaceBytes);/* 做循環拷貝 */ } gettimeofday(&tmEnd, NULL); /* 獲取拷貝完成後的系統時間 */ dRet = ((double)(tmEnd.tv_sec * 1000000 - /* 獲取前後耗時時間 */ tmStart.tv_sec * 1000000 + tmEnd.tv_usec - tmStart.tv_usec))/1000000; return dRet; /* 返回耗時 */}
3.4.6 結果輸出函數
使用簡單的printf即可完成輸出函數,具體實現如程序清單 36所示。
程序清單 36
/********************************************************************************** ** 函數名稱: reqOut ** 功能描述: 結果輸出函數 ** 輸 入: dTm 拷貝耗時 ** dSize 拷貝內容大小 ** 輸 出: NONE ** 調用模塊: NONE **********************************************************************************/static void reqOut (double dTm, double dSize, int iType){ switch (iType) { case WRITETYPE: printf("MEMWRITE\t"); /* 顯示操作類型 */ break; case READTYPE: printf("MEMREAD \t"); /* 顯示操作類型 */ break; case CPTYPE: printf("MEMCPY \t"); /* 顯示操作類型 */ break; default: return ; } printf("CPLOOPS: %d\t", CPLOOPS); /* 顯示拷貝次數 */ printf("Elapsed: %.5f\t", dTm); /* 顯示拷貝耗時 */ printf("MiB: %.5f\t", dSize); /* 顯示拷貝數據大小 */ printf("Handle: %.3f MiB/s\n", dSize/dTm * CPLOOPS); /* 顯示拷貝速率 */ return; }