一、写在测试之前的
程序测试磁盘速度应该考虑到几点:
- 首先是内存的存在。内存作为读写的缓冲区,写入一个文件到磁盘时fflush() 会将写入数据从用户态转为核态,只有核态命令fsync() 才能够将数据真正的刷新的硬盘。内存作为缓存在读时可能影响更大。
- 其次是磁盘缓存,磁盘缓存是非易失性的,速度接近于内存,但容量一般在16MB~64MB 之间。会对写入速度的测试有较大影响。比如对于64MB 缓存硬盘,只写入一个小于64MB 文件后计算速度会非常大。
- 不同磁道上扇区密度不同。如下左图是比较旧磁盘采用的磁道上扇区分法,这样的在同样一圈磁道上扇区数相同,内外径不同磁道上磁头旋转一圈读取的数据量相同,缺点就在于外径扇区没有充分利用较大的磁道。而右边采用的近似等线密度的扇区分区,外径上磁道比内径上磁道具有更多的扇区,也就是说磁头在外径磁道上旋转一周的时间会读取更多的数据,这点在后面的实验中也有证实。
- 还有一个需要考虑到得是文件系统的影响,文件系统决定了文件的放置,很大程度上决定了测试的读写性能,比如Linux ext 文件系统就和windows 的文件系统不同(因为不需要碎片整理了)。在下面大部分测试中都使用的是打开文件、写文件、关闭文件,然后对同一个文件重复同样流程。
二、磁盘写速度
实验用的磁盘是服务器1k5 320GB的SATA 硬盘,空余空间较大,在260GB 左右。
- 首先是1KB~1MB 文件写入速度,三次测试表明在280KB 和600KB 左右写入速度有一个峰值,原因还不详。
- 其次需要指出的是:为了避免头次写入磁盘缓存给速度的影响(第一次写入的文件会较快),在测速计时之前应写入大于磁盘缓存大小的数据,接着立即开始测速。
- 1MB~1GB 文件写入速度,可以看到几点比较有意思的:
- 磁盘写入速度呈现明显的周期性变化,这是因为两个原因:一、磁盘顺序写入时由外径磁道逐渐向内径磁道写入。二、内径扇区少,相同转速下写入速度慢。这个周期性变化在读速度中也有体现。
- 当写入文件超过越500MB 后,随着文件大小的增大,速度变慢,周期性规律变弱。
在图中每一个周期写入的数据和磁盘剩余空间相近,这点后面将估算。
上图在测试中每个文件写入次数较少,时间上更短,所以在图上周期跨度较大。
开始观察到在近200MB 左右写入速度为变化最大的周期点,我们以180MB~220MB 数据大小做写入测试,排除周期性变化为文件数据大小的影响。
- 180MB~220MB 文件写入速度测试。下图中三同颜色代表三次测试结果,可以看出即使是相同大小文件在不同测试中速度也不相同,呈现时间周期的变化,与文件大小无关。
这是上面实验在时间轴上的速度图,时间周期性变化更加明显了。
如果我们用X 轴时间对写入速度做一个积分就可以得到每一个周期写入的数据量了,但我们希望采用更聪明的方法。从1MB~1GB 中速度变化最剧烈的192MB 文件大小为例测试速度。在每一个周期中192 MB 文件共被写入137X10X192MB ≈ 260GB 近似为磁盘剩余容量。
下图是精确时间的写入速度周期性变化图。
三、磁盘读速度
为了消除内存缓存的影响,我们将2GB 内存内容写到80 个文件中,共计160GB 数据,通过一次性读取这160GB 数据测试硬盘读取速度。
- 首先看看这2GB 文件写入速度就比较慢了,而且看不出周期性变化的端倪。
- 接着是读取速度
四、相关代码
- 写入速度测试(1MB~1GB 文件写入)
1: #include <;stdio.h>
2: #include <;stdlib.h>
3: #include <;sys/time.h>
4: #include <;time.h>
5: #include <;string.h>
6:
7: FILE * fp;
8: int gettimeofday(struct timeval *tv,struct timezone *tz);
9:
10: double dt(struct timeval end,struct timeval start){
11: double tm;
12: tm=((end.tv_sec-start.tv_sec)*1000.0)+((end.tv_usec-start.tv_usec)*1.0)/1000.0;
13: return tm;
14: }
15:
16: void test(unsigned long size,unsigned long count){
17: unsigned long i;
18: char* pp;
19: struct timeval start,end;
20:
21: i=0;
22: pp=(char*)malloc(size+1);
23: gettimeofday(&start,NULL);
24: do{
25: if((fp=fopen("test.tmp","w"))==NULL){
26: printf("cann't create file!\n");
27: }
28: fwrite(pp,size,1,fp);
29: fclose(fp);
30: ++i;
31: }while(i<count);
32: gettimeofday(&;end,NULL);
33: printf("%3.1lf MB each time \t%3.2f MB/S \t%5.3lf ms\t%lu times\n",size/(1024*1024.0),(((count*size)/(1024.0*1024))/(dt(end,start)/1000.0)),dt(end,start),count);
34: free(pp);
35: }
36:
37: int main(void){
38: time_t start,end;
39: unsigned long count;
40: unsigned long size;
41: int i=1;
42:
43: test(64*1024*1024,2);
44: printf("++++++++++++++++++++++++++++++\n");
45: while(i>;0){
46: size=2*1024*1024;
47: count=512;
48: while(size<;=1024*1024*1024){
49: test(size,count);
50: size=size+2*1024*1024;
51: count=(unsigned long)((1024*1024*1024)/size);
52: }
53: --i;
54: }
55: }
- 先写入80 X 2GB 文件再读的代码
1: #include <;stdlib.h>
2: #include <;stdio.h>
3: #include <;sys/time.h>
4:
5: FILE * fp;
6: int gettimeofday(struct timeval *tv,struct timezone *tz);
7:
8: tm=((end.tv_sec-start.tv_sec)*1000.0)+((end.tv_usec-start.tv_usec)*1.0)/1000.0;
9: return tm;
10: }
11:
12: void init_str(char (*file_str)[8]){
13: int i;
14: for(i=0;i<80;i++){
15: sprintf(&;file_str[i][0],"temp%02d",i);
16: }
17: }
18:
19: void write_all(unsigned long long write_size){
20: unsigned long i;
21: char* pp;
22: int k;
23: char file_str[80][8];
24: struct timeval head,start,end;
25: double delta;
26:
27: pp=(char*)malloc(write_size+1);
28: init_str(file_str);
29: printf("2");
30: gettimeofday(&head,NULL);
31: for(k=0;k<80;k++){
32: gettimeofday(&;start,NULL);
33: if((fp=fopen(file_str[k],"w"))==NULL){
34: printf("cann't create the file!\n");
35: }
36: fwrite(pp,write_size,1,fp);
37: fclose(fp);
38: gettimeofday(&;end,NULL);
39: delta=dt(end,start)/1000;
40: printf("File:\t%s\tTime:\t%lf s\tSpeed:\t%3.1lf MB/S\t\n",file_str[k],delta,2048.0/delta);
41: }
42: gettimeofday(&;end,NULL);
43: delta=dt(end,head)/1000;
44: printf("All files for Time: %lf s\t Speed: %lf MB/S\n",delta,160*1024/delta);
45: free(pp);
46: }
47:
48: void read_all(int read_size){
49: unsigned long i;
50: char* pp;
51: int k;
52: char file_str[80][8];
53: struct timeval head,start,end;
54: double delta;
55:
56: pp=(char*)malloc(read_size+1);
57: init_str(file_str);
58: gettimeofday(&;head,NULL);
59: for(k=0;k<80;k++){
60: gettimeofday(&;start,NULL);
61: if((fp=fopen(file_str[k],"r"))==NULL){
62: printf("cann't read the file");
63: }
64: while(!feof(fp)){
65: fread(pp,read_size,1,fp);
66: }
67: gettimeofday(&;end,NULL);
68: delta=dt(end,start)/1000;
69: printf("File:\t%s\tTime:\t%lf s\tSpeed:\t%3.1lf MB/S\t\n",file_str[k],delta,2048.0/delta);
70: }
71: delta=dt(end,head)/1000;
72: printf("All files for Time: %lf s\t Speed: %lf MB/S\n",delta,160*1024/delta);
73: }
74: int main(void){
75: int read_size;
76: unsigned long long write_size=1024;
77: int i=1;
78:
79: printf("+++++++++++++++++++++++++++++++++++++++++++++++\n");
80: printf("| Write 160 GB files, 2 GB each file,tempxx |\n");
81: printf("+++++++++++++++++++++++++++++++++++++++++++++++\n");
82: write_size=write_size*1024*2048;
83: write_all(write_size);
84: printf("+++++++++++++++++++++++++++++++++++++++++++++++\n");
85: printf("| read 160 GB files |\n");
86: printf("+++++++++++++++++++++++++++++++++++++++++++++++\n");
87: read_size=64*1024*1024;
88: read_all(read_size);
89: return 1;
90: }
如果横轴不是时间,如何能说“写入速度”是周期性变化?
因为这个影响周期的不是时间,而是和磁盘容量有关