0%

验证“探寻C++最快的读取文件的方案"

本文接上一篇文章————探寻C++最快的读取文件的方案
仔细看了一下原文,发布时间是五年前,五年时间,无论是硬件还是软件环境都有了巨大的进步,我决定动手实现一下。

首先编写了一个生成生成随机数的程序,代码如下:

1
2
3
4
5
6
7
8
9
10
11
#include<cstdio>                       
#include<ctime>
#include<cstdlib>
int main ()
{
freopen("data.txt","w",stdout);
srand(time(0));
for(int i=0;i<1000000;i++)
printf("%d ",rand());
return 0;
}

生成一千万个随机数,并存储在data.txt中,约54MB。
编写使用scanf读取数据的程序,代码如下:

1
2
3
4
5
6
7
8
9
10
11
#include<cstdio>                                               
#include<ctime>
int a[10000000];
int main ()
{
int start = clock();
freopen("data.txt","r",stdin);
for(int i=0;i<10000000;i++)
scanf("%d",&a[i]);
printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
}

执行之后,使用时间为1.18s,相比于原文的2.01秒,缩短了一截,然后测试一下使用cin输入的情况,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include<cstdio>
#include<ctime>
#include<iostream>
int a[10000000];
int main ()
{
int start = clock();
freopen("data.txt","r",stdin);
for(int i=0;i<10000000;i++)
std::cin>>a[i];
printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
}

cin的使用时间为4.67s,比scanf更长,但是相比于原文的6.38s还是短得多。
然后取消cinstdin之间的同步之后,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<cstdio>
#include<ctime>
#include<iostream>
int a[10000000];
int main ()
{
int start = clock();
std::ios::sync_with_stdio(false);
freopen("data.txt","r",stdin);
for(int i=0;i<1000000;i++)
std::cin>>a[i];
printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
}

时间大幅缩短,为1.24s,与scanf很接近了。
然后按原文测试读入整个文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>                                              
#include<ctime>
#include<cstdio>
const int MAXN = 10000000;
const int MAXS = 60*1024*1024;
int numbers[MAXN];
char buf[MAXS];

void analyse(char *buf, int len =MAXS)
{
int i;
numbers[i=0]=0;
for(char *p=buf;*p && p-buf<len;p++)
if(*p == ' ')
numbers[++i]=0;
else
numbers[i]=numbers[i]*10+*p-'0';
}

void fread_analyse()
{
freopen("data.txt","rb",stdin);
int len = fread(buf,1,MAXS,stdin);
buf[len]='\0';
analyse(buf,len);
}

int main ()
{
int start = clock();
fread_analyse();
printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
return 0;
}

时间如原文一般,大幅缩短,我这里测试得到0.37s,使用read测试,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<iostream>                                             
#include<ctime>
#include<cstdio>
#include<unistd.h>
#include<fcntl.h>
const int MAXN = 10000000;
const int MAXS = 60*1024*1024;
int numbers[MAXN];
char buf[MAXS];

void analyse(char *buf, int len =MAXS)
{
int i;
numbers[i=0]=0;
for(char *p=buf;*p && p-buf<len;p++)
if(*p == ' ')
numbers[++i]=0;
else
numbers[i]=numbers[i]*10+*p-'0';
}

void read_analyse()
{
int fd = open("data.txt",O_RDONLY);
int len = read(fd,buf,MAXS);
buf[len]='\0';
analyse(buf,len);
}

int main ()
{
int start = clock();
read_analyse();
printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
return 0;
}

测试时间为0.31s,有所进步,不过不是非常明显。
调用mmap,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<iostream>                                                      
#include<ctime>
#include<cstdio>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
const int MAXN = 10000000;
const int MAXS = 60*1024*1024;
int numbers[MAXN];
char buf[MAXS];

void analyse(char *buf, int len =MAXS)
{
int i;
numbers[i=0]=0;
for(char *p=buf;*p && p-buf<len;p++)
if(*p == ' ')
numbers[++i]=0;
else
numbers[i]=numbers[i]*10+*p-'0';
}

void mmap_analyse()
{
int fd = open("data.txt",O_RDONLY);
int len = lseek(fd,0,SEEK_END);
char *mbuf = (char *) mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
analyse(mbuf,len);
}

int main ()
{
int start = clock();
mmap_analyse();
printf("%.3lf\n",double(clock()-start)/CLOCKS_PER_SEC);
return 0;
}

运行结果才让我大跌眼镜,虽然我没有眼镜

达到0.49s,与原文不符(原文中使用mmnp耗时更短,在我的测试中,耗时变长了),可能是代码与原作者的不一样,原作者只给出一部分代码,而测试需要写出完整的代码,可能我写的代码有问题。
以上测试结果在腾讯云上进行,因为原作者当时的硬件条件可能比不上我所使用的环境,我在树莓派 3B和我自己的电脑上测试了一下,所有平台硬件信息如下:

平台/硬件和软件信息 Cent OS Raspberry Windows
CPU 1 core Broadcom BCM2837 1.2GHz intel Core 5200u 2.2GHz
RAM 1GB 1GB 12GB
Gcc 4.8.5 4.9.2 5.3.0
PS: 这里忽略了硬盘的性能,理论上来说,硬盘的性能肯定能影响读写速度,只是没有较好的方法比较三个平台的硬盘性能,只能作罢。
测试结果汇总如下:
方法/平台/耗时(s) Cent OS Raspberry Windows(本机) Ubuntu(本机)
scanf 1.180 14.786 4.488 1.158
cin 4.670 61.255 13.026 4.309
cin取消同步 1.240 7.694 8.086 1.135
fread 0.37 3.503 0.327 0.284
read 0.31 2.975 0.370 0.285
mmap 0.49 5.945 NULL 0.447

生命重在折腾