上一篇博文介绍了如何获取磁盘的基本参数 磁盘序列号,转速,通电时间,温度等等,其实我们更关心的是性能,读写性能。很多开源工具都能测试磁盘性能,iozone,bonnie,bonnie++,fio等等,都是不错的工具,本文介绍bonnie++。
bonnie++,顾名思义也得知道有bonnie的存在。bonnie这个程序存在一些问题,比如对超过2G大小的文件不支持,所以Russel Coker大神写了bonnie++,bonnie++算是一个比较知名的的benchmark测试工具,可以用来测试硬盘以及文件系统的性能。想去下载源码的可以到 http://www.coker.com.au/bonnie++/ ,想去瞻仰一下Coker大神的, http://www.coker.com.au/russell/ 这是他的网站,大神写的一手好博客,被我果断收藏了。首先是下载及安装,目前最新的版本是1.03e:
- wget http://www.coker.com.au/bonnie++/bonnie++-1.03e.tgz
这些选项当中
-f 表示fast mode,快速模式,因为bonnie++写有putc的测试,读有getc的测试,不用block,每次读或者写一个字节的测试,如果加了-f这个option,这个字节的测试就跳过了。
-D open的时候,带上O_DIRECT标志,对应的代码逻辑在bon_io.cpp
- flags = O_RDWR | O_CREAT | O_EXCL;
- if(m_use_direct_io)
- {
- flags |= O_DIRECT;
- }
-s 是用来制定测试用的文件的大小,如果你没有加 -s这个option,系统会默认使用2倍内存大小的文件,比如我的内存是2G,那么bonnie会使用4G的文件来测试性能,原因在于减少缓存的影响。对Linux下文件和page cache感兴趣的筒子可以看我的这篇博文: file和page cache的一些事儿 。代码逻辑在bonnie++.cpp
- if(globals.ram && !setSize)
- {
- if(file_size < (globals.ram * 2))
- file_size = globals.ram * 2;
- // round up to the nearest gig
- if(file_size % 1024 > 512)
- file_size = file_size + 1024 - (file_size % 1024);
- }
常用的指定-s选项的是这样的:
- bonnie++ -d /home/disk_test/ -s 16000:1048576 -r 1024 -u root
- case 's':
- {
- char *sbuf = strdup(optarg);
- char *size = strtok(sbuf, ":");
- file_size = atoi(size);
- char c = size[strlen(size) - 1];
- if(c == 'g' || c == 'G')
- file_size *= 1024;
- size = strtok(NULL, "");
- if(size)
- {
- int tmp = atoi(size);
- c = size[strlen(size) - 1];
- if(c == 'k' || c == 'K')
- tmp *= 1024;
- globals.set_chunk_size(tmp);
- }
- setSize = true;
- }
- break;
如果filesize超过1G,会存储到多个文件,最大filesize不能超过1000G.
- #define MaxIOFiles 1000
#define IOFileSize 1024
-
- m_chunks_per_file(Unit / m_chunk_size * IOFileSize)
- ....
if(file_size > IOFileSize * MaxIOFiles)
usage();
usage();
- bonnie++ -u manu
- Version 1.03e ------Sequential Output------ --Sequential Input- --Random-
- -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
- Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP
- manu 4G 33516 92 70332 11 23182 6 35877 83 83433 10 127.8 0
- ------Sequential Create------ --------Random Create--------
- -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
- files /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP
- 16 +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++
- manu,4G,33516,92,70332,11,23182,6,35877,83,83433,10,127.8,0,16,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++
- For every test two numbers are reported, the amount of work done (higher numbers are better) and the percentage of CPU time taken to perform the work (lower numbers are better). If a test completes in less than 500ms then the output will be displayed as "++++". This is because such a testresult can't be calculated accurately due to rounding errors and I would rather display no result than a wrong result.
OK,这个++解释完了,我们继续。这个输出也太丑了,完全没有考虑到人的感受啊,幸好提供了bon_csv2html工具:
- echo manu,4G,33516,92,70332,11,23182,6,35877,83,83433,10,127.8,0,16,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++,+++++,+++ |bon_csv2html >>bon_result.htm
我的磁盘的性能就测试出来了:
Sequential Output下的 Per Char是值用putc方式写,毫无疑问,因为cache的line总是大于1字节的,所以不停的骚扰CPU执行putc,看到cpu使用率是92%.写的速度是33MB/s,还不错。
Sequential Output 是按照block去写的,明显CPU使用率就下来了,速度也上去了,越是70MB/s。
Sequential Input下的Per Char是指用getc的方式读文件,速度是35.8MB/s,CPU使用率是83%。
Sequential Input下的block是指按照block去读文件,速度是83MB/s,CPU使用率是10%。
另一个比较有意思的Random Seeks,含义是随机寻址。但是实际上,不仅是seek,还包括100%的读和10%的写, 这项性能指标是127.8次/s。对应如下代码
- #define UpdateSeek (10)
-
- if(rc == 1 && ticket) do
- {
- bool update;
- if( (lseek_count++ % UpdateSeek) == 0)
- update = true;
- else
- update = false;
- if(file->doseek(rand() % num_chunks, update) )
- exit(1);
- }
- ....
- int
- CFileOp::doseek(long where, bool update)
- {
- if (seek(where, SEEK_SET) == -1) // 100%的seek
- return io_error("lseek in doseek");
- if (read_block(PVOID(m_buf)) == -1) //100%的读
- return io_error("read in doseek");
-
- /* every so often, update a block */
- if (update) //10%的写
- { /* update this block */
-
- /* touch a byte */
- m_buf[int(rand()) % m_chunk_size]--;
- if(seek(where, SEEK_SET) == -1)
- return io_error("lseek in doseek update");
- if (write_block(PVOID(m_buf)) == -1)
- return io_error("write in doseek");
- if(m_sync)
- {
- if(fsync(m_fd[m_file_ind]))
- {
- fprintf(stderr, "Can't sync file.n");
- return -1;
- }
- }
- } /* update this block */
- return 0;
- }
- zcav -f /dev/sda
参考文献:
1 GPFS performace report (uses bonnie++ command)
2 How To Get Rid Of The Plus Signs In Bonnie++ Output
3 使用Bonnie++进行系统IO性能测试