一、生成测试数据
先把1000万个测试数据写入data.txt中
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
const int num=10000000;
int main()
{
ofstream fout("data.txt");
clock_t t1, t2;
t1 = clock();
for(int i = 0; i < num; i++)
{
fout << i << ' ';
}
t2 = clock();
cout << "Running time: " << t2 - t1 << " ms" << endl;
return 0;
}
运行结果:
4764 ms
电脑的配置为i7处理器,16G内存,运行时间接近5秒,生成的data.txt文件大小为77040k,即77M左右。
二、cin与scanf的效率比较
(一)
#include<iostream>
#include<ctime>
#include<cstdio>
using namespace std;
const int num=10000000;
int main()
{
freopen("data.txt", "r", stdin);
int i, x;
clock_t t1, t2;
t1 = clock();
for(i = 0; i < num;i++)
{
cin >> x;
}
t2 = clock();
cout << "Runtime of cin: " << t2 - t1 << " ms" << endl;
clock_t t3, t4;
t3 = clock();
for(i = 0; i < num;i++)
{
scanf("%d", &x);
}
t4 = clock();
cout << "Runtime of scanf: " << t4 - t3 << " ms" << endl;
return 0;
}
运行结果:
Runtime of cin: 16363 ms
Runtime of scanf: 13753 ms
分析:
1 先要把测试数据文件data.txt拷贝到当前工程目录下
2 stdin是C语言的标准输入流,表示先把data.txt中的数据读取到标准输入流里面,然后用cin >> x的时候,就不会要求用户从控制台输入数据,而是从stdin中读取数据
3 从运行结果可以看出,输入1千万个数据,scanf的效率只比cin快一点。这与通常说的scanf效率远高于cin不符。(互联网随便一搜索都是scanf效率比cin高)
这是因为,这里我使用的集成开发环境(IDE)是CodeBlocks,内置了G++,G++是C++的一种编译器,G++对cin和cout做了优化,会大幅提高cin和cout的效率。

(二)对cin进行加速
下面两行代码可以提升cin和cout的效率
ios::sync_with_stdio(true);
cin.tie(0);
完整代码为:
#include<iostream>
#include<ctime>
#include<cstdio>
using namespace std;
const int num=10000000;
int main()
{
freopen("data.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
int i, x;
clock_t t1, t2;
t1 = clock();
for(i = 0; i < num;i++)
{
cin >> x;
}
t2 = clock();
cout << "Runtime of cin: " << t2 - t1 << " ms" << endl;
clock_t t3, t4;
t3 = clock();
for(i = 0; i < num;i++)
{
scanf("%d", &x);
}
t4 = clock();
cout << "Runtime of scanf: " << t4 - t3 << " ms" << endl;
return 0;
}
运行结果:
Runtime of cin: 4925 ms
Runtime of scanf: 13777 ms
可以看到,加了两句代码后,cin的效率有了大幅提高,从16秒缩短为5秒!
三、sync_with_stdio与cin.tie分析
(一)ios::sync_with_stdio()
sync_with_stdion的参数默认值为true,表示cin与scanf同步,cout与printf同步。
同步(sync)是什么意思呢?
比如有几个人排队去打水,水龙头只有一个。那么只有前面的人打完水,后面的人才能打水,这种情况下,必然是排在前面的人比后面的人先打完水。
与同步对应的是异步(async),异步又是什么回事呢?
比如有几个人排队去打水,水龙头有好多个。那么排在前面的人未必会比排在后面的人先打完水。比如排在第1位的人选了1号水龙头,排在第2位的人紧接着选了2号水龙头,假如2号水龙头出水的速度远大于1号水龙头,那么排在第2位的人,会比排在第1位的人先打完水。
同步和异步只有在cin和scanf(或cout与printf)混用的情况下才有意义。默认情况下执行的是ios::sync_with_stdio(true)
,表示cin和scanf是按被程序调用的顺序先后执行的。
cin >> a;
scanf("%d", &b);
cin >> c;
scanf("%d", &d);
这里的输入顺序一定是a, b, c, d。
若改为ios::sync_with_stdio(false)
,则输入顺序不一定是a,b,c,d。
当使用了ios::sync_with_stdio(false)
,cin和scanf,cout和printf就不要再混用了。因为异步可能会导致意想不到的后果。
(二)cin.tie(0)
这里的tie表示绑定,在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush(即清空缓存),这样会增加IO(输入输出)负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
默认情况下cout << "123" << endl;
也可以写成cin.tie() << "123" << endl;
cin.tie(0)等价于cin.tie(NULL),表示不与任何输出流相绑定,即解绑了默认的cout。
若此时再使用cin.tie() << "123" << endl;
编译器会报错。
可通过下面的程序来加深理解:
#include <iostream>
#include <fstream>
#include <windows.h>
using namespace std;
int main()
{
ostream *prevstr;
ofstream ofs; // 文件输出流
ofs.open("test.out");
cout << "Example of tie method\n"; //直接输出至控制台窗口
*cin.tie() << "Insert into cout\n"; // 空参数调用返回默认的output stream,也就是cout
prevstr = cin.tie(&ofs); // cin绑定新的输出流指针ofs,并返回上一次绑定的输流指针即cout
*cin.tie() << "Insert into file\n"; // ofs,输出到文件test.out中
cin.tie(prevstr); // 恢复原来的output stream,即cout
*cin.tie() << "Insert into cout again\n";
ofs.close(); //关闭文件输出流
return 0;
}
运行结果:
控制台上打印出
Example of tie method
Insert into cout
Insert into cout again
还生成了test.out文件,内容为
Insert into file
四、cout与printf的效率比较
(一)在控制台输出10万个数据进行测试
#include<iostream>
#include<cstdio>
#include<ctime>
using namespace std;
const int num=100000;
int main()
{
int i;
clock_t t5, t6;
t5 = clock();
for(i = 0; i < num;i++)
{
cout << i << ' ';
}
t6 = clock();
clock_t t7, t8;
t7 = clock();
for(i = 0; i < num;i++)
{
printf("%d ", i);
}
t8 = clock();
cout << endl << "Runtime of cout: " << t6 - t5 << " ms" << endl;
cout << "Runtime of printf: " << t8 - t7 << " ms" << endl;
return 0;
}
运行结果:
Runtime of cout: 10852 ms
Runtime of printf: 19753 ms
可以看出,cout运行效率比printf要高,这是G++编译器对cin和cout优化的结果。
(二)对cout进行加速
#include<iostream>
#include<cstdio>
#include<ctime>
using namespace std;
const int num=100000;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int i;
clock_t t5, t6;
t5 = clock();
for(i = 0; i < num;i++)
{
cout << i << ' ';
}
t6 = clock();
clock_t t7, t8;
t7 = clock();
for(i = 0; i < num;i++)
{
printf("%d ", i);
}
t8 = clock();
cout << endl << "Runtime of cout: " << t6 - t5 << " ms" << endl;
cout << "Runtime of printf: " << t8 - t7 << " ms" << endl;
return 0;
}
运行结果:
Runtime of cout: 546 ms
Runtime of printf: 19597 ms
可见加速后,cout的运行速度更快了。
五、结论
(1)scanf/printf需要格式化符号%d, %f, %c之类的,这是不如cin/cout方便的地方
(2)cout在控制小数位输出时,很不方便。需要如此操作
#include<iomanip>
cout << fixed << setprecision(5) << endl; // 输出5位小数
(3)对于某些优化过cin和cout的编译器(比如G++)而言,cin/cout的运行效率比scanf/printf高。
但是对于没做过优化的编译器,则是scanf/printf的效率大大高于cin/cout。互联网上能搜到的文章,几乎都是这种情况。这与本篇的实验结果恰好相反。
(4)对于非算法比赛而言,用cin/cout或scanf/printf无所谓。
(5)但是对于算法比赛而言,因为数据量大,经常会导致超时(TLE--Time limit exceeded)。此时可统一使用scanf/printf,或者使用加了ios::sync_with_stdio(false); cin.tie(0)
的cin/cout。
TopCoder & Codeforces & AtCoder交流QQ群:648202993
更多内容请关注微信公众号
wechat_public_header.jpg
网友评论