摸鱼第一,干活第二

It's time for BOOM!(ノ≧∀≦)ノ

【黑科技】数据读入姿势大比拼

如何让你的程序运行时间-1s


几种常用的读入姿势

1、iostream

可能OIer都会说这玩意就是个垃圾,但这只能说明你们没有正确使用。

2、fstream

iostream使用stdin来读入,而fstream则通过成员函数open()来打开一个文件读入。

3、scanf

当使用iostream吃到苦头了之后所以OIer都会转向使用的东西。

4、fscanf

与scanf的区别在于fscanf需要一个用fopen()打开的文件指针来代替stdin。

5、getchar

当scanf也不管用的时候,另一种快速读取算法使用的就是getchar()。

6、fread

香港来的。


比拼结果先写在前面

测试数据为1M(1,000,000)个整数读入,每个方法测试100次取平均时间,单位为秒。

《【黑科技】数据读入姿势大比拼》

注:r为文本模式读取,rb为二进制读取

对于SYN与非SYN的解释

以r模式读取时,对于fread、fstream、getchar、iostream来说,非SYN模式会使运行时间缩短,特别是iostream,快了几乎10倍!甚至比scanf与getchar还快!

除了fscanf和scanf反而坑爹地用了更长的时间。

所以说不会用iostream的人反而更吃亏。。。

回归正题,什么是SYN?SYN为英语单词同步(synchronization)的缩略词,在C++里指的是C++的ios类所使用的缓冲区与C使用的cstdio缓冲区同步。ios类的缓冲区供cin、cout等使用,cstdio的缓冲区供scanf、printf等使用,而让他们两者同步其实上是为了能使程序员同时使用cin、cout、scanf和printf等函数,既然两者是完全不同的东西,那么同步时必定会进行一些数据交换,而这些万恶的数据交换导致了iostream异常缓慢!同时也会拉低cstdio的速度。

但如果取消了SYN再输出会发生什么呢?【滑稽】你会看见printf与cout输出顺序会鬼畜。。。往往printf跑到前面去了,因为cstdio的缓冲区刷新比ios类的快。。。。

取消SYN只需要一句神秘代码:

1
ios::sync_with_stdio(false);

对于r与rb的解释

可以看到对于一些方法,使用rb可以获得更快的速度。

非SYN模式下,当文件以r模式打开时,对于windows系统的文件,换行处使用了两个字符——回车符与换行符(\r\n),内部会自动把它们处理成一个字符——换行符(\n),这是需要消耗计算资源的(也就是时间),而以rb模式打开时,程序不会对其处理,所以可以节省一些时间。

可能你会问了,fread、getchar、fstream和iostream为什么会更慢了?那是因为这些方法内部处理的问题,使用了rb模式就意味着需要读入的字符更多了,处理速度也就被拉慢了。


跳出速度分析利弊

先从总速度第一的fread开谈,fread方法本质上是使用fread代替了getchar的使用,我们可以看看它的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
char read(){//代替了getchar()
    static char buff[0xFFFFF],*p1=buff,*p2=buff;//一个手动缓存
    return ((p1==p2)&&(p2=(p1=buff)+fread(buff,1,0xFFFFF,fin),p1==p2))?EOF:*p1++;
}
int read_int(){//与普通getchar读入相似
    char ch=read();
    int t=0;
    while((ch<'0')||(ch>'9'))
        ch=read();
    while((ch>='0')&&(ch<='9'))
        t=t*10+ch-'0',ch=read();
    return t;
}
tmp=read_int();

在函数read()中,fread提前读入了“一块”文件,相当于只是执行了一次内存复制,而如果换成getchar则用一次就要复制一次内存,当然会更慢。不同题目使用不同的手动缓存大小效果也会不同,所以缓存大小也是速度的决定因素。

fread方法对于数字处理与getchar一样是比较友善的,但是遇到了输入非常复杂的,看在编程难度上就还是和getchar一样放在一旁把。。。。

推荐使用的题目:纯数字读入题或不太复杂的读入题(使用r以获得优良的输入体验)

 

fstream比iostream更快emmmmm。。。比起通过重定向stdin与直接操作文件来说也挺正常对不对?更多不能用fread的题目就用这个吧,至少复杂数据读入比较友好。【滑稽】

1
2
3
ifstream ifs("test.txt",ios_base::in);
ifs>>tmp;
ifs.close();

推荐使用的题目:复杂读入题(使用r以获得优良的输入体验)

 

getchar大家都会打就不谈了,很简单,对复杂数据极不友好。

推荐使用的题目:纯数字读入题或不太复杂的读入题(使用r以获得优良的输入体验)

 

iostream大家也会了,只要取消SYN就可以解决所有问题。

推荐使用的题目:复杂读入题(使用r以获得优良的输入体验)

 

fscanf并不常用,只会让你的代码看起来很长。

1
2
3
FILE *fin=fopen("test.txt","rb");
fscanf(fin,"%d",&tmp);
fclose(fin);

推荐使用的题目:不推荐使用(使用syn+rb也无法获得优良的输入体验)

 

scanf挂在这里打脸说iostream慢的人。

推荐使用的题目:不推荐过来人使用(使用rb也无法获得优良的输入体验)

【END】

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code