前言
相信不少C++初学者对于在算法竞赛中输入输出应该用C++的cin&cout
,还是用C风格的scanf&printf
感到疑惑,对我而言,我是很喜欢cin&cout
简洁但又安全的性质,但是之前也听说过cin&cout
的是效率低于scanf&printf
的说法,那么在面对大量数据I/O时scanf&printf
可能解决cin&cout
超时的问题,那么我们只能抛弃cin&cout了吗?关于这个问题的答案,我认为作为理工生,我们应该用严谨的数据来说话。
我利用代码生成了三千万个【1,100】以内的随机数,并且将分别用cin
和scanf
输入到一个数组中。
const int MAXNUM = 30000000;
int RandomNumber;
srand((unsigned)time(NULL));
for (int i = 0; i < MAXNUM; i++)
{
RandomNumber = rand() % 100 + 1;
}
int start = clock();
for (int i = 0; i < MAXNUM; i++)
{
//scanf("%d ", &number[i]);
cin >> number[i];
}
printf("scanf用时:%.3lf\n", double(clock() - start) / CLOCKS_PER_SEC);
for(int i = 0;i < 100000; i++)
{
printf("%d\n", i);
//cout << i << endl;
}
我在Windows系统GCC编译器下得出的用时数据(单位:秒),且已验证过不存在偶然性:
scanf | cin |
---|---|
6.836 | 11.303 |
同时,我用printf和cout分别输出十万个正整数。
printf | cout |
---|---|
6.172 | 11.509 |
于是可以得出结论:cin&cout的确在效率上是低于scanf&printf的(当然在我得出的数据中表明并没有网上流传的差距那么夸张)。
这时又有一个新的问题:为什么有这么大的差距呢?这是因为C++为了和C保持同步、在混用 printf&scanf 和 cin&cout时的时候不发生混乱,将它的输入/输出流绑到了一起。
而我们可以通过std::ios::sync_with_stdio(false);
指令关闭同步(这里要注意:当关闭同步之后为了确保准确,不要使用 printf&scanf了),除此之外我们还可以通过std::cin.tie(nullptr);
获取cin更优的性能(解除std :: cin和std :: cout之间的绑定,来降低IO的负担使效率提升)。那么在对iostream优化之后的 printf&scanf 和 cin&cout效率差距又有多大呢?我通过上述的实验得出以下数据:
scanf | cin(优化后) |
---|---|
6.836 | 2.695 |
printf | cout (优化后) |
---|---|
6.172 | 6.178 |
可以看见在关闭同步之后cin的效率已经是高于scanf了,并且cout的速度与printf的速度也相差无几,那我们还能不能继续优化呢?
我们注意到通常在用cout输出的时候,更习惯去使用endl,它既可以达到换行的需求又可以刷新缓冲区,然而在如此高度的循环下它一直对缓冲区的操作却降低了效率,所以将endl换成’\n’cout的效率将会起飞(顺带一提,我在开启同步的条件发现endl或是\n对cout效率的影响并不大,不知道是不是我的问题,希望各位指出)。
printf | cout(‘\n’) |
---|---|
6.172 | 1.106 |
如此一来我们再一次得出结论:在同步开启时,scanf&printf的效率要高于cin&cout;当同步关闭时,cin&cout的效率要高于scanf&printf。
当然我也看过一些帖子中表述在(ACM)关闭同步后cin&cout
依旧会超时,而换成scanf&printf
不会,我认为这是算法的效率问题,提升自己算法的效率更加重要。事实上在一些场景中,scanf&printf或是cin&cout的使用不应该是刻板的,应该择优选择(尤其是遇到一些要求格式输入输出的题目,scanf&printf还是很香的)。
当然为了预防当scanf&printf也会超时的情况,这里再给出一个C++下快读快写的一个模板,毕竟快读快写的效率真的很高。
/*快读快写*/
template <typename T>
inline T read()//这里加inline是为了解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题
{
//自定义的类型T
register T sum = 0, f1 = 1;//f1是标志位
register int ch = getchar();
//如果输入的是负数
for(; !isdigit(ch); ch = getchar())
{
if(ch == '-')
{
f1 = -1;
}
}
for(; isdigit(ch); ch = getchar())
{
sum = sum * 10 + ch - '0';
}
return sum * f1;
}
//int a = read<int>();
template <typename T>
inline void write(T x)
{
static int stk[20];//模拟压栈
int top = 0;
do{
stk[top++] = x % 10;
x /= 10;
}while(x);
while(top)
{
putchar(stk[--top] + '0');
}
}
//write(a);
这里再附上优化后的cin&cout与快读快写的效率比较:
read | cin |
---|---|
0.485 | 2.695 |
write | cout |
---|---|
18.908 | 1.106 |
总结&补充
至于有些帖子上总结的“数据量超过1e7就用cin&cout,小于就用scanf&printf”的言论我也将MAXNUM调整到到了一百万去测试,我得出的结果是优化之后的cin&cout
的效率(0.092s)依旧是高于scanf&printf
(0.222s)与通常的cin&cout
的(0.374s)。
超过1e7的数据在上文中展示过了,scanf&printf的确是占有优势的,而通过优化之后的cin&cout效率更高,至于到底应该用scanf&printf还是优化之后的cin&cout,我认为还是看个人爱好与题目要求吧(在项目中,方便的话我还是推荐使用cin&cout,因为更安全)。
部分参考自文章