引言
个别时候,嵌入式设备的DDR除了常规的遍历读写和压力测试外,会有提供读写带宽的需求,下面介绍一种没有精确要求的测法,参考自http://github.com/raas/mbw
原理主要分三部分:memcpy(内存拷贝),dumb(字符拷贝),memblock(内存块)拷贝,通过计算拷贝指定内容的时间来得出内存拷贝速度,时间由系统函数gettimeofday()获得。
gettimeofday():
它获得的时间精确到微秒(1e-6 s)量级。在一段代码前后分别使用gettimeofday可以计算代码执行时间:
struct timeval tv_begin, tv_end; gettimeofday(&tv_begin, NULL); foo(); gettimeofday(&tv_end, NULL);
函数执行成功后返回0,失败后返回-1,错误代码存于errno中。
/* * vim: ai ts=4 sts=4 sw=4 cinoptions=>4 expandtab */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/time.h> #include <time.h> #include <string.h> #include <unistd.h> /* how many runs to average by default */ #define DEFAULT_NR_LOOPS 10 /* we have 3 tests at the moment */ #define MAX_TESTS 3 /* default block size for test 2, in bytes */ #define DEFAULT_BLOCK_SIZE 262144 /* test types */ #define TEST_MEMCPY 0 #define TEST_DUMB 1 #define TEST_MCBLOCK 2 /* version number */ #define VERSION "1.4" /* * MBW memory bandwidth benchmark * * 2006, 2012 Andras.Horvath@gmail.com * 2013 j.m.slocum@gmail.com * (Special thanks to Stephen Pasich) * * http://github.com/raas/mbw * * compile with: * gcc -O -o mbw mbw.c * * run with eg.: * * ./mbw 300 * * or './mbw -h' for help * * watch out for swap usage (or turn off swap) */ void usage() { printf("mbw memory benchmark v%s, https://github.com/raas/mbw\n", VERSION); printf("Usage: mbw [options] array_size_in_MiB\n"); printf("Options:\n"); printf(" -n: number of runs per test (0 to run forever)\n"); printf(" -a: Don't display average\n"); printf(" -t%d: memcpy test\n", TEST_MEMCPY); printf(" -t%d: dumb (b[i]=a[i] style) test\n", TEST_DUMB); printf(" -t%d: memcpy test with fixed block size\n", TEST_MCBLOCK); printf(" -b <size>: block size in bytes for -t2 (default: %d)\n", DEFAULT_BLOCK_SIZE); printf(" -q: quiet (print statistics only)\n"); printf("(will then use two arrays, watch out for swapping)\n"); printf("'Bandwidth' is amount of data copied over the time this operation took.\n"); printf("\nThe default is to run all tests available.\n"); } /* ------------------------------------------------------ */ /* allocate a test array and fill it with data * so as to force Linux to _really_ allocate it */ long *make_array(unsigned long long asize) { unsigned long long t; unsigned int long_size=sizeof(long); long *a; a=calloc(asize, long_size); if(NULL==a) { perror("Error allocating memory"); exit(1); } /* make sure both arrays are allocated, fill with pattern */ for(t=0; t<asize; t++) { a[t]=0xaa; } return a; } /* actual benchmark */ /* asize: number of type 'long' elements in test arrays * long_size: sizeof(long) cached * type: 0=use memcpy, 1=use dumb copy loop (whatever GCC thinks best) * * return value: elapsed time in seconds */ double worker(unsigned long long asize, long *a, long *b, int type, unsigned long long block_size) { unsigned long long t; struct timeval starttime, endtime; double te; unsigned int long_size=sizeof(long); /* array size in bytes */ unsigned long long array_bytes=asize*long_size; if(type==TEST_MEMCPY) { /* memcpy test */ /* timer starts */ gettimeofday(&starttime, NULL); memcpy(b, a, array_bytes); /* timer stops */ gettimeofday(&endtime, NULL); } else if(type==TEST_MCBLOCK) { /* memcpy block test */ char* aa = (char*)a; char* bb = (char*)b; gettimeofday(&starttime, NULL); for (t=array_bytes; t >= block_size; t-=block_size, aa+=block_size){ bb=mempcpy(bb, aa, block_size); } if(t) { bb=mempcpy(bb, aa, t); } gettimeofday(&endtime, NULL); } else if(type==TEST_DUMB) { /* dumb test */ gettimeofday(&starttime, NULL); for(t=0; t<asize; t++) { b[t]=a[t]; } gettimeofday(&endtime, NULL); } te=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000; return te; } /* ------------------------------------------------------ */ /* pretty print worker's output in human-readable terms */ /* te: elapsed time in seconds * mt: amount of transferred data in MiB * type: see 'worker' above * * return value: - */ void printout(double te, double mt, int type) { switch(type) { case TEST_MEMCPY: printf("Method: MEMCPY\t"); break; case TEST_DUMB: printf("Method: DUMB\t"); break; case TEST_MCBLOCK: printf("Method: MCBLOCK\t"); break; } printf("Elapsed: %.5f\t", te); printf("MiB: %.5f\t", mt); printf("Copy: %.3f MiB/s\n", mt/te); return; } /* ------------------------------------------------------ */ int main(int argc, char **argv) { unsigned int long_size=0; double te, te_sum; /* time elapsed */ unsigned long long asize=0; /* array size (elements in array) */ int i; long *a, *b; /* the two arrays to be copied from/to */ int o; /* getopt options */ unsigned long testno; /* options */ /* how many runs to average? */ int nr_loops=DEFAULT_NR_LOOPS; /* fixed memcpy block size for -t2 */ unsigned long long block_size=DEFAULT_BLOCK_SIZE; /* show average, -a */ int showavg=1; /* what tests to run (-t x) */ int tests[MAX_TESTS]; double mt=0; /* MiBytes transferred == array size in MiB */ int quiet=0; /* suppress extra messages */ tests[0]=0; tests[1]=0; tests[2]=0; while((o=getopt(argc, argv, "haqn:t:b:")) != EOF) { switch(o) { case 'h': usage(); exit(1); break; case 'a': /* suppress printing average */ showavg=0; break; case 'n': /* no. loops */ nr_loops=strtoul(optarg, (char **)NULL, 10); break; case 't': /* test to run */ testno=strtoul(optarg, (char **)NULL, 10); if(testno>MAX_TESTS-1) { printf("Error: test number must be between 0 and %d\n", MAX_TESTS-1); exit(1); } tests[testno]=1; break; case 'b': /* block size in bytes*/ block_size=strtoull(optarg, (char **)NULL, 10); if(0>=block_size) { printf("Error: what block size do you mean?\n"); exit(1); } break; case 'q': /* quiet */ quiet=1; break; default: break; } } /* default is to run all tests if no specific tests were requested */ if( (tests[0]+tests[1]+tests[2]) == 0) { tests[0]=1; tests[1]=1; tests[2]=1; } if( nr_loops==0 && ((tests[0]+tests[1]+tests[2]) != 1) ) { printf("Error: nr_loops can be zero if only one test selected!\n"); exit(1); } if(optind<argc) { mt=strtoul(argv[optind++], (char **)NULL, 10); } else { printf("Error: no array size given!\n"); exit(1); } if(0>=mt) { printf("Error: array size wrong!\n"); exit(1); } /* ------------------------------------------------------ */ long_size=sizeof(long); /* the size of long on this platform */ asize=1024*1024/long_size*mt; /* how many longs then in one array? */ if(asize*long_size < block_size) { printf("Error: array size larger than block size (%llu bytes)!\n", block_size); exit(1); } if(!quiet) { printf("Long uses %d bytes. ", long_size); printf("Allocating 2*%lld elements = %lld bytes of memory.\n", asize, 2*asize*long_size); if(tests[2]) { printf("Using %lld bytes as blocks for memcpy block copy test.\n", block_size); } } a=make_array(asize); b=make_array(asize); /* ------------------------------------------------------ */ if(!quiet) { printf("Getting down to business... Doing %d runs per test.\n", nr_loops); } /* run all tests requested, the proper number of times */ for(testno=0; testno<MAX_TESTS; testno++) { te_sum=0; if(tests[testno]) { for (i=0; nr_loops==0 || i<nr_loops; i++) { te=worker(asize, a, b, testno, block_size); te_sum+=te; printf("%d\t", i); printout(te, mt, testno); } if(showavg) { printf("AVG\t"); printout(te_sum/nr_loops, mt, testno); } } } free(a); free(b); return 0; }
交叉编译
运行测试
常用参数
-q 隐藏日志
-n 10 测试次数
128 内存大小(单位是M)
执行结果如下图,其中主要关注AVG一行,内存分配速率越大,性能越好:
#./ddrtest -n 10 128 Long uses4bytes.Allocating 2*33554432 elements = 268435456 bytes of memory. Using 262144 bytes as blocks for memcpy block copy test. Getting down to business... Doing 10 runs per test. Method:MEMCPY Elapsed:0.23141 MiB:128.00000 Copy: 553.133 MiB/s Metho0: MEMCPY Elapsed: 0.23103 MiB:128.00000 Copy: 554.029 MiB/s Method:| MEMCPY Elapsed:0.23167 MiB:128.00000 Copy:552.500 MiB/s Method:MEMCPY Elapsed:0.23131 MiB:128.00000 Copy: 553.380 MiB/s Method:MEMCPY Elapsed:0.23192 MiB:128.00000 Copy:551.917 MiB/s Method: MEMCPY Elapsed:0.23118 MiB:128.00000 Copy: 553.684 MiB/s Method;MEMCPY Elapsed:0.23176 MiB:128.00000 Copy:552.303 MiB/s Metno0:MEMCPY Elapsed: 0.23161 MiB:128.00000 Copy: 552.641 MiB/s 9 Method;MEMCPY Elapsed:0.23200 MiB:128.00000 Copy:551.731 MiB/s Method: MEMCPY Elapsed:0.23154 MiB:128.00000 Copy: 9 AVG Methog:MEMCPY Elapsed:0.23154 MiB:128.00000 Copy: