输入输出效率对比
前言
在OI竞赛中,几乎每道题都需要进行文件的读写操作。然而在非常紧的时间限制内,读写的时间也至关重要。目前可供使用的标准输入输出操作有如下几种:
- cin/cout
- cin/cout+取消同步
- scanf/printf
- 手写I/O优化
那么本文接下来将会对这几种方法进行简单的比较。
介绍
首先对各种输入输出方式给出简单的介绍。
cin/cout
这是C++标准库中提供的对读入输出流进行操作的对象,也是大部分C++程序所使用的读写方式。在流中需要开辟一个缓冲区,此处不过多赘述,但要知道的是这样的处理可以延长内存或者外部存储器的寿命,但这样也使得cin/cout的效率降低。
scanf/printf
这是C语言库中提供的格式化读入输出函数,相较于cin/cout要更加底层,效率要好一些,但是缺点是格式比较复杂。
cin/cout+取消同步
在默认情况下,为了保持同C语言程序的兼容性,可以在代码中同时使用cin/cout和scanf/printf两种读写方法。但是如果取消这种同步性,cin/cout的效率会有提高。
使用时只要在代码开始运行时加上这样一句:
1
| ios::sync_with_stdio(false);
|
手写I/O优化
手写读入输出优化是OI竞赛中常见的技巧,对于大量读入或者输出的题目可以大幅提高速度。但是当然对存储器不是很友好,所以一般在其他的程序中不建议使用。
这里给出一种版本的模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| //读入优化
template<typename T>
void read(T& w)
{
char r;int f=1;
for(r=getchar();(r<48||r>57)&&r!='-';r=getchar());
if(r=='-')r=getchar(),f=-1;
for(w=0;r>=48&&r<=57;r=getchar())w=w*10+r-48;
w*=f;
}
//输出优化
template<typename T>
void write(T w)
{
if(w<0)w=-w;
if(w==0)putchar('0');
else
{
if(w>9)
write(w/10);
putchar(char(w%10+48));
}
}
|
比较
我们采取一种方式对这些读入输出优化进行量化的比较。
读入测试
数据
读入n个随机int类型整数(有正有负)
共8组数据,n(i)=10^(i-1)
程序
以下是进行读入测试的程序:
cin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,x;
int main()
{
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>x;
}
cout<<n<<endl;
return 0;
}
|
cin/sync
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,x;
int main()
{
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>x;
}
cout<<n<<endl;
return 0;
}
|
read/template
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
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
template<typename T>
void read(T& w)
{
char r;int f=1;
for(r=getchar();(r<48||r>57)&&r!='-';r=getchar());
if(r=='-')r=getchar(),f=-1;
for(w=0;r>=48&&r<=57;r=getchar())w=w*10+r-48;
w*=f;
}
int n,x;
int main()
{
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
read(x);
}
cout<<n<<endl;
return 0;
}
|
read/int
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
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
void read(int& w)
{
char r;int f=1;
for(r=getchar();(r<48||r>57)&&r!='-';r=getchar());
if(r=='-')r=getchar(),f=-1;
for(w=0;r>=48&&r<=57;r=getchar())w=w*10+r-48;
w*=f;
}
int n,x;
int main()
{
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
read(x);
}
cout<<n<<endl;
return 0;
}
|
scanf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,x;
int main()
{
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
scanf("%d",&x);
}
cout<<n<<endl;
return 0;
|
输出测试
数据
读入n,对于所有1<=i<=n,按顺序输出i,-i;
n[i]=10^(i-1)
程序
cout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,x;
int main()
{
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
cout<<i<<endl;
cout<<-i<<endl;
}
return 0;
}
|
cout/sync
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,x;
int main()
{
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;++i)
{
cout<<i<<endl;
cout<<-i<<endl;
}
return 0;
}
|
printf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,x;
int main()
{
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
printf("%d\n%d\n",i,-i);
}
return 0;
}
|
write/template
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
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
template<typename T>
void write(T w)
{
if(w<0)putchar('-'),w=-w;
if(w>9)write(w/10);
putchar(char(w%10+48));
}
int n,x;
int main()
{
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
write(i);
putchar('\n');
write(-i);
putchar('\n');
}
return 0;
}
|
write/int
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
| #include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
void write(int w)
{
if(w<0)putchar('-'),w=-w;
if(w>9)write(w/10);
putchar(char(w%10+48));
}
int n,x;
int main()
{
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
{
write(i);
putchar('\n');
write(-i);
putchar('\n');
}
return 0;
}
|
结果
方式 | 输入测试1 | 输入测试2 | 输出测试1 | 输出测试2 |
---|
cin/cout | 39.15 | 39.21 | 23.97 | 19.31 |
cin/cout+nosync | 4.54 | 4.43 | 17.72 | 15.13 |
scanf/printf | 2.79 | 2.83 | 6.42 | 6.49 |
read/write(int) | 0.94 | 1.02 | 3.13 | 3.04 |
read/write(template) | 0.98 | 0.88 | 2.99 | 3.07 |
(注:测试用的pentiumIII处理器(机房配置)大概有点老,我自己i7-7500U的笔记本cin只用6s……不过这样差距更直观了)
可以看出。使用模板的手写读入优化和直接使用int的手写读入优化效率不相上下,scanf要比它们慢3倍左右,取消同步的cin是4.5倍,而直接的cin是40倍!
同样,手写的读入输出优化不相上下。printf为它们的2倍左右,取消同步的cout是5倍左右。但是对于输出,取消同步并没有太大的优势。
小结
可以看出,手写IO优化的效率非常高,而cin/cout的效率非常低。这是一个普遍的规律,兼容性越好的一般效率会比较差。C语言的scanf/printf表现中规中矩。
所以,对于输入输出数据量比较大的题目,使用手写IO优化有时可以起到比较好的效果,一般情况下scanf/printf也足够使用,但是cin/cout在算法竞赛中可能不是那么实用。
打包:一次无聊的实验 密码: 6jv5