转自:
http://chino.taipei/note-2016-0311C-%E7%9A%84%E8%BC%B8%E5%87%BA%E5%85%A5cin-cout%E5%92%8Cscanf-printf%E8%AA%B0%E6%AF%94%E8%BC%83%E5%BF%AB%EF%BC%9F/
有打竞赛或经常刷题的人,一定有遇过用cin/cout结果TLE,换成scanf/printf就AC的情况。
难道cin/cout真的比较慢吗?为什么C++要做出一个比C还要更慢的输入输出介面呢?
我们来看看cin/cout的效率到底怎么样。
以下都是个人的观察,有错的话请留言告知QAQ,本人很废还请鞭小力一点。
开始前,cin/cout是什么?
首先,我们先来看一下cin/cout和scanf/printf的差别,前者是物件,后者是函数。
函数很简单,就是定义一个函数,然后他会把里面出现%的地方取代掉,而物件则是重载了shift运算子<<,>>,其实真的很直观,就丢进cout跟从cin拿出来嘛~,而且也不用管型别,因为编译器会帮你找运算子规则。
这里我们发现,型别是编译器处理的,和执行时完全没有关系(别再说cin/cout慢是因为要判断型别了),而且自由度更高,可以自己定义。
那cin/cout到底慢再哪里呢?
我们先用time指令做个小实验,在Ubuntu 14.04笔电对一个档案写入1e7的random整数,这里的程式码都是简化的code。
for(int i = 0; i < (int)1e7; i++){
printf("%d\n",rand());
// vs
for(int i = 0; i < (int)1e7; i++){
cout<<rand()<<endl;
实验三次,printf的时间分别是,
1.760 s
2.677 s
1.865 s
看起来很优秀,那cout呢?
15.921 s
15.188 s
15.685 s
发生了什么事?怎么慢成这样!
优化1:sync_with_stdio 函数:和stdio同步
我已经看到那些笃定cin/cout不好的人偷笑的表情了,但是事情别说的太早,我们先看一下C++ Reference对于cin/cout的说明,我们发现了一个函数:std::ios_base ::sync_with_stdio(false),他是这样说的
Toggles on or off synchronization of all the iostream standard streams with their corresponding standard C streams if it is called before the program performs its first input or output operation.
If called once an input or output operation has occurred, its effects are implementation-defined.
By default, iostream objects and cstdio streams are synchronized (as if this function was called with true as argument).
With stdio synchronization turned off, iostream standard stream objects may operate independently of the standard C streams (although they are not required to), and mixing operations may result in unexpectedly interleaved characters.
看起来,cin/cout预设必须要跟stdin/stdout同步,所以必须做额外的运算,注意要是关掉了,scanf/printf就不能用了(如果用了,而且跟cin/cout混用,可能会吃到奇怪的东西),那我们试着把他关掉看看。
ios_base::sync_with_stdio(false);
for(int i = 0; i < (int)1e7; i++){
cout<<rand()<<endl;
结果:
13.120 s
14.958 s
15.165 s
看起来变快了两秒,甚至根本没变快,还是很慢啊…(你看看,自己慢还怪scanf/printf拖慢你)
等等,我们还忘了一个东西,endl。
优化2:endl 和 flush 物件:cout的缓冲区优化
什么是endl,他是一个定义好的物件,在cout上给cout换行用的,那他跟<<’\n’有什么差别呢?
原来,cout用了一个类似优化的设计,叫作缓冲区(由作业系统实作),所有的输出都会先进到缓冲区里,直到缓冲区满了才会清空缓冲区并把字串输出到stdout之类的输出串流,难怪没有跟stdout同步会出错。
而当一般人写程式的时候,输出当然希望程式会把东西印到萤幕上,但是如果缓冲区还没满,我们就看不到结果了!
怎么办呢? cout有一个物件叫作flush(用法跟endl一样),做的事情就是强迫清空缓冲区,并输出到串流。
但是为什么平常出学C++的人都没有打过flush呢?原因有几个,一个是Windows8以前的Windows CMD会自动清空缓冲区(或是根本没有QAQ),另外一个主要的原因就是,其实endl就是<<’\n’<
ios_base::sync_with_stdio(false);
for(int i = 0; i < (int)1e7; i++){
cout<<rand()<<'\n';
结果:
2.765 s
1.708 s
1.713 s
太震惊了,去掉了endl之后,cout的速度已经和printf差不多快了! !整整快了12秒! !
原来效率就是在这种情况下不见的,那为什么要作endl这种物件呢?
我们看看下面的实验。
附注,其实printf也是有缓冲区的,只是他预设是到满了才会清空。平常在console可以看到输出是因为OS帮忙我们把缓冲区清掉了
优化3:cin.tie(0):cin和cout绑定
我们先吃一个数字进来,再把他输出
for(int i = 0; i < (int)1e7; i++){
scanf("%d\n",&a);
printf("%d\n",a+1);
for(int i = 0; i < (int)1e7; i++){
cin>>a;
cout<<a+1<<endl;
scanf/printf的时间:
2.579 s
3.994 s
3.241 s
而cin/cout:
19.970 s
不意外,那加上关闭同步的话?
ios_base::sync_with_stdio(false);
for(int i = 0; i < (int)1e7; i++){
cin>>a;
cout<<a+1<<endl;
结果:
16.575 s
快了几秒,不算太意外,那去掉endl呢?
ios_base::sync_with_stdio(false);
for(int i = 0; i < (int)1e7; i++){
cin>>a;
cout<<a+1<<'\n';
结果:
16.408 s
什么! !完全没有变快啊! ? (你看看,看来就算cout很快,cin还是很慢啊)
等等,已经说过cin没道理比scanf慢这么多,所以我们来看看发生了什么事。
既然和有endl一样快,我们可以合理怀疑是cin/cout又清空缓冲区了。
我们试试看下面的例子,我们先吃进一个阵列,再丢出来。
ios_base::sync_with_stdio(false);
for(int i = 0; i < (int)1e7; i++){
cin>>A[i];
for(int i = 0; i < (int)1e7; i++){
cout<<A[i]+1<<'\n';
结果:
2.918 s
2.811 s
3.062 s
太神奇了,竟然变得什至比scanf/printf还要快了,发生了什么事?
看起来是cin/cout交错使用导致的,我们看一下C++ Reference对于cin的说明,我们发现一个函数tie()。
std::ios::tie
Get/set tied stream
The tied stream is an output stream object which is flushed before each i/o operation in this stream object.
这样就清楚了,cin预设绑住了cout,而被绑住的ostream会在istream要输入时被flush。
那我们试试看把cin/cout解绑,我们可以透过传一个NULL(也可以用0)进入cin.tie()来让cin绑住空的ostream。
我们加上一行cin.tie(0)再来看刚刚的例子。
ios_base::sync_with_stdio(false);
cin.tie(0);
for(int i = 0; i < (int)1e7; i++){
cin>>a;
cout<<a+1<<'\n';
结果:
2.956 s
2.889 s
3.509 s
时间已经和吃进阵列差不多了,剩下的差距已经在误差范围内了。
为什么要有tie这个设计呢?
我曾经看过一些说法,一种是说,因为我们有时候可能要写一些console应用程式,如果我们要使用者输入一些值的时候可能要先输出一些提示讯息像是「请输入一个数字:」然后才用cin输入,要是上面那一句话没有被flush到萤幕上的话,使用者就看不到了,而且你可能不想要换行,就算加<
我们试着把数字范围放大到1e8看看,
scanf/printf:
30.722 s
29.428 s
cin/cout:
27.052 s
27.097 s
cin/cout的表现已经比scanf/printf好了,事实上,我之前看过一篇文章(现在找不到了QQ)里面有一张图表,上面显示了cin/cout的效率在1e7之后就会开始超越scanf /printf了,当然这有很多的因素在里面,而且iostream使用的记忆体也比scanf/printf高出一些。
但总结来说cin/cout和scanf/printf比起来更快最主要的原因,是cin/cout可以在编译时期就把型别等等编译进去,而scanf/printf则要在执行时期处理,所以cin /cout就算比scanf/printf快,我觉得也不会很奇怪。
使用cin时发现很多时候换成scanf就AC,否则就TLE。这是因为cin需要将输入数据存入缓存再输出导致时间变长,而在使用cin前在main中加入以下两句,cin、cout的时间就与scanf、printf相差不多了
ios::sync_with_stdio(0);
cin.tie(0);
事实上,我们进行刷题的时候,对于输入量较大的题,用cin有时会时间超时,改成 scanf 之后 可以正常AC;
对此有两个解释,其一由于cin与stdin有兼容性,时刻要与stdin保持同步,防止文件指针混乱,同时cout与stdout 保持一致,防止输出混乱,因此导致时间增加。其二,cin将数据存入缓冲区,cout输出时要从缓冲区取出数据,而scanf与printf直接调用系统I / O,所需时..
scanf是用指针操作的,没有类型安全机制。比如一个char类型数据你就可以用%f获得输入,而不会报错,但在运行时会出现异常。
cin是自动判断你的变量类型,比如一个char数据只能用默认的char方法取数据。
标准流对象cin/cout为了普适性,继承体系很复杂,所以在对象的构造等方面会影响效率,因此总体效率比较低。
cin有许多额外的开销,可以用std::ios::sync_with_stdio(false)或ios::sync_with_stdio(false);cin.tie(0);,这样就可以取
scanf和printf比cin和cout要快很多,有时候会因为这个超时,所以虽然不知道为什么,但以后还是尽量用scanf和printf吧(还是要根据情况,如果数据比较大比较多就用省时的)格式化输出效率比较高,但是******写代码麻烦。流输出操作效率稍低,但********书写简便。...
(上次因为用cin和cout导致时间超限一直不知道咋不对劲,后来发现这这个问题,改成scanf和printf就AC了,所以,以后。scanf与printf。scanf与printf。
基本说明:
cin代表标准输入设备,使用提取运算符 “>>” 从设备键盘取得数据,送到输入流对象cin中,然后送到内存。
cin是输入流,cout是输出流,重载了">>"、"<<"运算符,包含在头文件中。
先把要输出的东西存入缓冲区,再输出,导致效率降低,cin是自动判断你的变量类型,比如一个char数据只能用默认的char方法取数据。
sc...
cin和scanf的效率差异
最近在在OJ上水了一道算法题,提交总是TLE(time limit:1000MS)总以为是自己的算法不够优化,无论怎么努力始终过不了,有种崩溃的感觉。无意间把cin换成了scanf竟然AC了,时间114MS。难道cin和scanf差距效率差异真的这么大吗?cin读入效率比scanf慢这个大家应该都是知道的,但为什么慢。博主结合请教大神和去Google,百度总结出一点
玩竞赛的同学应该发现了C++中直接调用cout、cin的效率要比printf和scanf的效率要低。
要解决这个问题,只需要在前面加上一句
std::ios::sync_with_stdio(false);
这是因为cout、cin要先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段语句可以来打消iostream的输入输出缓存,可以节省时间,使效率与scan...
昨天在OJ上看到一个很水的题,题意就是两个递增序列,输出合并后新序列的中值(详细描述可参见我的另一篇文章http://hi.baidu.com/i5love1you9/blog/item/250f57d671b6f41aa08bb721.html)。当时也闲来无事,于是决定动手写写。刚开始也没怎么在意,认为该题随便都能AC。可提交的结果却TLE了,当时就郁闷了,这算法不可能会有问题啊,不就是一个简