一、生成测试数据

先把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

分析:
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的效率。

小朋友学C++(22):cin与scanf,cout与printf的效率比较_ios

(二)对cin进行加速

下面两行代码可以提升cin和cout的效率

::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

可以看到,加了两句代码后,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

四、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

可以看出,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

可见加速后,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
更多内容请关注微信公众号

小朋友学C++(22):cin与scanf,cout与printf的效率比较_ios_02