1. 内存性能测试需求
SylixOS作为大型实时操作系统,在各个领域当中都会有重要应用。其中,在高性能运算领域需要较高的内存读写性能,因此我们需要有一个直观,跨平台的内存测试软件来给出SylixOS在各个平台的内存性能数据。
2. 内存性能测试原理
内存性能无非内存访问的读写速度,内存读写速度的测试需要在全速的内存的操作前后记录操作耗时,根据操作耗时和操作的内存大小得出操作的速度。原理如公式公式 2 1所示,V为内存操作速度,S为内存操作的容量,T为内存操作的耗时。
公式 2-1 速度计算公式
3. 内存性能测试程序设计与实现
由于考虑到需要对比其他操作系统,尽量采用通用的内存操作接口来实现相关程序。测试程序流程图如所示。
图 3‑1 内存测试程序流程图
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,具体程序如程序清单 3‑1所示。
程序清单 3‑1
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,多次操作来获取操作的时间。具体实现如程序清单 3‑3所示。
程序清单 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函数拷贝两个内存数据的效率。具体实现如程序清单 3‑5所示
程序清单 3‑5
/**********************************************************************************
** 函数名称: 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即可完成输出函数,具体实现如程序清单 3‑6所示。
程序清单 3‑6
/**********************************************************************************
** 函数名称: 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;
}