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