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;}