在一次无意的做题提交中,使用cin导致TLE(Time Limit Exceeded),而使用scanf没有导致TLE,而引发了我对cin和scanf的思考,由此产生了这篇文章
cin/cout和scanf/printf速度对比
在windows平台上测试会有较大差距,例如在code blocks上,cin读取的时间总是远远小于scanf,而在visual studio 2022中,cin读取的时间和scanf相差无几,因此,本人测试是在manjaro linux系统下,使用g++编译器得到的结果
具体测试代码:
#include <iostream>
using namespace std;
#define TEST "一百万.txt"
const int MAXN = (int)1e6;
void scanf_read(void){
freopen(TEST, "r", stdin);
for (int i = 0; i < MAXN; i++)
scanf("%d", &numbers[i]);
}
void cin_read(void){
ios::sync_with_stdio(false);
cin.tie(0);
freopen(TEST, "r", stdin);
for (int i = 0; i < MAXN; i++)
cin >> numbers[i];
}
void printf_show(void){
for (int i = 0; i < MAXN; i++)
printf("%d\n", numbers[i]);
}
void coutn_show(void){
ios::sync_with_stdio(false);
for (int i = 0; i < MAXN; i++)
cout<< numbers[i] << '\n';
}
void coutendl_show(void){
ios::sync_with_stdio(false);
for (int i = 0; i < MAXN; i++)
cout << numbers[i] << endl;
}
int main(){
int start = clock();
scanf_read();
printf("%.3lf\n", double(clock() - start) / CLOCKS_PER_SEC);//注意在使用禁用同步后,请将该printf替换为用cout输出
}
cin与scanf对比
测试一(网友测试数据):
经过用于读取标准输入的数字列表和XOR所有数字测试程序的测试,得出结论:
iostream version: 21.9 seconds //使用cin
scanf version: 6.8 seconds //使用scanf
iostream with sync_with_stdio(false): 5.5 seconds //关掉C++ I/O功能 保持 与C I/O功能的同步
//关闭功能同步代码
std::ios::sync_with_stdio(false);//函数介绍http://www.cplusplus.com/reference/ios/ios_base/sync_with_stdio/
测试二(我在linux下测试):
//1e7数据规模下各方法读取时间
scanf("%d", &numbers[i]); 0.69s
cin >> numbers[i]; 2.20s
//仅关闭同步
ios::sync_with_stdio(false); 0.71s
//关闭同步且使用cin.tie(0)
ios::sync_with_stdio(false);
cin.tie(0); 0.67s
显而易见,cin赢了! 事实证明,这种内部同步/刷新通常会减慢iostream i/o的速度。如果我们不混合stdio和iostream,我们可以关闭它,然后cin是最快的。
比较合理的解释:默认情况,cin与stdin总是保持同步的,也就是说这两种方法可以混用,而不必担心文件指针混乱,同时cout和stdout也一样,两者混用不会输出顺序错乱。正因为这个兼容性的特性,导致cin有许多额外的开销,如何禁用这个特性呢?只需一个语句 std::ios::sync_with_stdio(false);,这样就可以取消cin于stdin的同步了,此时的cin就与scanf差不多 了。
另一种解释: cout在输出时总是要先将输出的存入缓存区。而printf直接调用系统进行IO,它是非缓存的。所以cout比printf慢。(这种解释,我没有验证)
相关网站
cout和printf对比
先介绍用到的方法
ends: 终止字符串,即在末尾加入“\0”或者空白字符
flush: 刷新缓冲区
endl: 换行加刷新输出缓冲区
测试结果
//5e6数据规模输出测试
printf("%d\n", numbers[i]); 7.89s
cout+'\n' 8.16s
cout+endl 7.89s
cout+ends 0.97s
//使用ios::sync_with_stdio(false);
cout+'\n' 3.19s
cout+endl 8.09s
cout+ends 0.75s
//使用ios::sync_with_stdio(false); cout.tie(0);
cout+'\n' 3.10s
cout+endl 7.69s
cout+ends 0.81s
总结
在数据规模大的情况下,没有禁用iostrema I/O
与stdio I/O
的同步情况下
- cin和scanf对比
- 未禁用同步
推荐使用scanf,scanf的读取速度明显高于cin - 禁用同步
推荐使用cin,在C++编程中尽量少使用C语言的代码
ios::sync_with_stdio(false); 0.71s ios::sync_with_stdio(false); cin.tie(0); 0.67s
两者速度对比具有
cin.tie(0);
代码的更快,因此建议写cin.tie(0);
- 未禁用同步
-
cout和printf对比
- 未禁用同步
两者速度基本相同,用谁都行,但是,在C++中也要尽量少使用C语言代码 - 禁用同步
推荐使用
cout + '\n'
形式,使用cout + endl
的话,速度与printf相差无几,而禁用同步时间则会大大减小//ios::sync_with_stdio(false); cout+'\n' 3.19s cout+endl 8.09s cout+ends 0.75s //使用ios::sync_with_stdio(false); cout.tie(0); cout+'\n' 3.1s cout+endl 7.69s cout+ends 0.81s
两者速度对比
cout.tie(0);
更快,因此建议写cout.tie(0);
- 未禁用同步
sync_with_stdio()
这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起。
tie()
tie是将两个stream绑定的函数,空参数的话返回当前的输出流指针。在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
ACM应用
在ACM里,经常出现数据集超大造成 cin TLE的情况。这时候大部分人认为这是cin的效率不及scanf的错,甚至还上升到C语言和C++语言的执行效率层面的无聊争论。其实像上文所说,这只是C++为了兼容而采取的保守措施。我们可以在IO之前将stdio解除绑定,这样做了之后要注意不要同时混用cout和printf之类。
在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
原来而cin,cout之所以效率低,是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段代码可以来打消iostream的输入 输出缓存,可以节省许多时间,使效率与scanf与printf相差无几.
说白了,这两句加上就可以提高C++代码输入输出执行效率使得和C相差无几,甚至优于C
找到了一个使用cin/cout但不使用scanf/printf以及sync_with_stdio()函数会导致超时的例子
1249. 亲戚 – AcWing题库