一、内存性能测试程序集
在讲memtester之前,痞子衡先给大家科普一下Linux系统下常用的内存性能测试工具,它们分别是mbw、memtester、lmbench、sysbench。这几个测试工具(程序)各有侧重点:1内存带宽测试工具 --mbw;
2内存压力测试工具 --memtester;
3内存综合性能测试工具 --lmbench;
4内存申请与读写速度测试工具 --sysbench;
二、memtester程序
memtester是Simon Kirby在1999年编写的测试程序(v1版),后来由Charles Cazabon一直维护更新(v2及之后版本),主要面向Unix-like系统,官方主页上介绍的是“A userspace utility for testing the memory subsystem for faults.”,其实就是为了测试内存(主要DDR)的读写访问可靠性(仅正确性,与速度性能无关),这是验证板级硬件设备必不可少的一项测试。整个memtester测试的视角就是从用户的角度来看的,从用户角度设立不同的测试场景即测试用例,然后针对性地进行功能测试,注意是从系统级来测试,也就是说关注的不单单是内存颗粒了,还有系统板级的连线、IO性能、PCB等等相关的因素,在这些因素的影响下,内存是否还能正常工作。2.1 获取程序
memtester程序的最新版本是4.5.0,早期的v1/v2/v3版本目前下载不到了,2012年Charles Cazabon重写了程序并发布了全新v4.0.0,此后一直不定期更新,v4.x也是当前最流行的版本。核心程序下载:http://pyropus.ca/software/memtester/
核心程序包下载后,在\memtester-4.5.0\下可找到源代码。详细源文件目录如下:1\memtester-4.5.0
2 \memtester.h
3 \memtester.c --主程序入口
4 \sizes.h --关于系统位数(32/64bit)的一些定义
5 \types.h --所用数据类型的定义
6 \tests.h
7 \tests.c --测试算法子程序
2.2 配置参数
memtester源码里的配置选项主要是如下五个宏:1/* 如下需用户自定义 */
2ULONG_MAX -- 确定系统是32bit还是64bit
3TEST_NARROW_WRITES -- 确定是否要包含8/16 bit写测试
4
5/* 如下借助linux头文件 */
6_SC_VERSION -- posix system版本检查
7_SC_PAGE_SIZE -- 内存page大小获取
8MAP_LOCKED -- Linux里mmap里的swap特性
2.3 程序解析
让我们尝试分析memtester主函数入口main,main()函数最开始都是一些输入参数解析,其实主要就是为了获取三个重要变量:内存测试起始地址、内存测试总长度、压力测试循环次数,有了这三个变量值之后便开始逐一跑tests.c文件里各项测试算法小函数: 1struct test {
2 char *name;
3 int (*fp)();
4};
5
6struct test tests[] = {
7 { "Random Value", test_random_value },
8 { "Compare XOR", test_xor_comparison },
9 { "Compare SUB", test_sub_comparison },
10 { "Compare MUL", test_mul_comparison },
11 { "Compare DIV",test_div_comparison },
12 { "Compare OR", test_or_comparison },
13 { "Compare AND", test_and_comparison },
14 { "Sequential Increment", test_seqinc_comparison },
15 { "Solid Bits", test_solidbits_comparison },
16 { "Block Sequential", test_blockseq_comparison },
17 { "Checkerboard", test_checkerboard_comparison },
18 { "Bit Spread", test_bitspread_comparison },
19 { "Bit Flip", test_bitflip_comparison },
20 { "Walking Ones", test_walkbits1_comparison },
21 { "Walking Zeroes", test_walkbits0_comparison },
22#ifdef TEST_NARROW_WRITES
23 { "8-bit Writes", test_8bit_wide_random },
24 { "16-bit Writes", test_16bit_wide_random },
25#endif
26 { NULL, NULL }
27};
28
29/* Function definitions */
30void usage(char *me) {
31 fprintf(stderr, "\n"
32 "Usage: %s [-p physaddrbase [-d device]] <mem>[B|K|M|G] [loops]\n",
33 me);
34 exit(EXIT_FAIL_NONSTARTER);
35}
36
37int main(int argc, char **argv) {
38 ul loops, loop, i;
39 size_t bufsize, halflen, count;
40 void volatile *buf, *aligned;
41 ulv *bufa, *bufb;
42 ul testmask = 0;
43
44 // 省略若干变量定义代码
45
46 printf("memtester version " __version__ " (%d-bit)\n", UL_LEN);
47 printf("Copyright (C) 2001-2020 Charles Cazabon.\n");
48 printf("Licensed under the GNU General Public License version 2 (only).\n");
49 printf("\n");
50
51 // 省略若干初始检查代码
52 // 从输入参数里获取physaddrbase计算出内存测试起始地址aligned
53 // 从输入参数里获取mem及B|K|M|G计算出内存测试总长度bufsize
54
55 halflen = bufsize / 2;
56 count = halflen / sizeof(ul);
57 bufa = (ulv *) aligned;
58 bufb = (ulv *) ((size_t) aligned + halflen);
59
60 // 压力测试的重要变量, loops即重复次数
61 for(loop=1; ((!loops) || loop <= loops); loop++) {
62 printf("Loop %lu", loop);
63 if (loops) {
64 printf("/%lu", loops);
65 }
66 printf(":\n");
67 printf(" %-20s: ", "Stuck Address");
68 fflush(stdout);
69
70 // 第一个测试 stuck_address
71 if (!test_stuck_address(aligned, bufsize / sizeof(ul))) {
72 printf("ok\n");
73 } else {
74 exit_code |= EXIT_FAIL_ADDRESSLINES;
75 }
76
77 // 遍历tests.c里的所有测试子程序
78 for (i=0;;i++) {
79 if (!tests[i].name) break;
80 if (testmask && (!((1 << i) & testmask))) {
81 continue;
82 }
83 printf(" %-20s: ", tests[i].name);
84 // 可以看到将内存测试总空间一分为二,传给子程序做处理的
85 if (!tests[i].fp(bufa, bufb, count)) {
86 printf("ok\n");
87 } else {
88 exit_code |= EXIT_FAIL_OTHERTEST;
89 }
90 fflush(stdout);
91 /* clear buffer */
92 memset((void *) buf, 255, wantbytes);
93 }
94 printf("\n");
95 fflush(stdout);
96 }
97}
tests.c文件里才是最核心的压力测试算法子程序,一共17个函数,涉及各种内存访问经验操作,具体可以看网上的一篇详细解析文章 https://www.jianshu.com/p/ef203c360c4f。
测试函数名 | 测试作用 |
---|---|
test_stuck_address | 先全部把地址值交替取反放入对应存储位置,然后再读出比较,重复n次 |
test_random_value | 等效test_random_comparison(bufa, bufb, count):数据敏感型测试用例 |
test_xor_comparison | 与test_random_value比多了个异或操作 |
test_sub_comparison | 与test_random_value比多了个减法操作 |
test_mul_comparisone | 与test_random_value比多了个乘法操作 |
test_div_comparison | 与test_random_value比多了个除法操作 |
test_or_comparison | 在test_random_comparison()里面合并了 |
test_and_comparison | 在test_random_comparison()里面合并了 |
test_seqinc_comparison | 是 test_blockseq_comparison的一个子集;模拟客户压力测试场景 |
test_solidbits_comparison | 固定全1后写入两个buffer,然后读出比较,然后全0写入读出比较;这就是Zero-One算法 |
test_blockseq_comparison | 一次写一个count大小的块,写的值是拿byte级的数填充32bit,然后取出对比,接着重复256次;也是压力用例,只是次数变多了; |
test_checkerboard_comparison | 把设定好的几组Data BackGround,依次写入,然后读出比较 |
test_bitspread_comparison | 还是在32bit里面移动,只是这次移动的不是单单的一个0或者1,而是两个1,这两个1之间隔着两个空位/td> |
test_bitflip_comparison | 也是32bit里面的一个bit=1不断移动生成data pattern然后,每个pattern均执行 |
test_walkbits1_comparison | 与test_walkbits0_comparison同理 |
test_walkbits0_comparison | 就是bit=1的位置在32bit里面移动,每移动一次就全部填满buffer,先是从低位往高位移,再是从高位往低位移动 |
test_8bit_wide_random | 以char指针存值,也就是每次存8bit,粒度更细; |
test_16bit_wide_random | 以unsigned short指针存值,也就是每次存16bit,不同粒度检测; |
2.4 结果格式
在Unix-like系统下使用make && make install命令进行编译可得到一个可执行的memtester,可以随便执行memtester 10M 1,即申请10M的内存测试1次,结果如下:[root@as150 ~] memtester 10M 1
memtester version 4.5.0 (64-bit)
Copyright (C) 2001-2020 Charles Cazabon.
Licensed under the GNU General Public License version 2 (only).
pagesize is 4096
pagesizemask is 0xfffffffffffff000
want 10MB (10485760 bytes)
got 10MB (10485760 bytes), trying mlock ...locked.
Loop 1/1:
Stuck Address: ok
Random Value: ok
Compare XOR: ok
Compare SUB: ok
Compare MUL: ok
Compare DIV: ok
Compare OR: ok
Compare AND: ok
Sequential Increment: ok
Solid Bits: ok
Block Sequential: ok
Checkerboard: ok
Bit Spread: ok
Bit Flip: ok
Walking Ones: ok
Walking Zeroes: ok
8-bit Writes: ok
16-bit Writes: ok
Done.
至此,内存读写正确性压力测试程序memtester痞子衡便介绍完毕了,掌声在哪里~~~本文转载自:痞子衡嵌入式
版本归原作者所有,仅供大家学习参考,如有侵权还请麻烦联系小哥删除,感谢
最 后
☞ ☞ ☞
☞ ☞ ☞ ☞