震惊!C++用不同的方式输入,时间相差20倍!原因竟然是……

本文比较了C++中cin、scanf及手写read函数在大量数据输入时的性能,并给出了优化建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

upd:之前的界面太丑,美化了一下

前言

众所周知,C++C++C++的流输入输出cincincincoutcoutcout是比较慢的。在比赛(尤其像OIOIOI这种有部分分的比赛)中,用cincincincoutcoutcout有可能超时,而意味着有些本该拿到的分就拿不到了。这里给出cincincinscanfscanfscanf,手写readreadread,优化手写readreadread的速度对比。

制造数据

先做一个数据生成器。用文件输出,将5000000个随机数保存在data.txt里,后面的程序都从data.txt中读取数据。
如下:

#include<stdio.h>
#include<stdlib.h>
int main() {
    freopen("data.txt","w",stdout);
    const int Size=5000000;
    for(int i=1; i<=Size; i++)
        printf("%d\n",rand());
    return 0;
}

cin的速度

先写出一个用cin读入这5000000个数的程序,用time.h里面的clock()函数算出程序运行用了多少时间。
程序如下:

#include<iostream>
#include<time.h>
#include<stdio.h>
using namespace std;
int main() {
    freopen("data.txt","r",stdin);
    const int Size=5000000;
    int a,b,n;
    a=clock();
    for(int i=1; i<=Size; i++) {
        cin>>n;
    }
    b=clock();
    printf("%d ms",b-a);
    return 0;
}

程序的运行结果为10667ms,即约10.7秒。
假设时限1秒,可算出当有500000个数据需要读入时,程序肯定就超时了。
所以,必须要想法将输入的速度优化。
cincincin的能够与scanfscanfscanf混用的原理,是开启了同步,大大拖慢了程序运行的时间(这个很多博客里也写过,就不详细讲了)。
于是,关闭同步再测(关闭同步用ios::sync_with_stdio(false)):
程序为


#include<iostream>
#include<time.h>
using namespace std;
int main() {
    freopen("data.txt","r",stdin);
    std::ios::sync_with_stdio(false);
    const int Size=5000000;
    int a,b,n;
    a=clock();
    for(int i=1; i<=Size; i++) {
        cin>>n;
    }
    b=clock();
    printf("%d ms",b-a);
    return 0;
}

运行结果:4927ms。速度已经提升到原来的两倍。
但是在NOILinuxNOI LinuxNOILinux的环境下,关闭流同步是编译不过的!
所以,比赛中需要用其他的优化。
就可以想到用scanf。

scanf的速度

scanfscanfscanf的速度比(不关闭同步的)cincincin的速度要快很多。
程序如下:

#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
int main() {
    freopen("data.txt","r",stdin);
    const int Size=5000000;
    int a,b,n;
    a=clock();
    for(int i=1; i<=Size; i++) {
        scanf("%d",&n);
    }
    b=clock();
    printf("%d ms",b-a);
    return 0;
}

运行结果:5232ms。
可见,scanfscanfscanf也比cincincin快一倍左右,但比关闭流输入的cincincin要慢一点。
但是,scanfscanfscanf碰见输入数据很多的情况,也是会TLE的。因为这时留给程序的计算时间已经不多了。

手写read的速度

getchargetchargetchar是一个快速读入字符的函数。由于其速度快,经常用它来手写readreadread函数。函数的思路是:遇到不相关的字符,跳过;遇到数字,则将它放进预先设置的一个整型变量的尾端。
程序如下:

#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
int read() {
    int x=0,f=1;    //x是返回的数字,f表示正负。
    char ch=getchar();
    while(ch<'0' || ch>'9') {
        if(ch=='-') f=-1;   //处理负数 
        ch=getchar();       //跳过这个字符 
    }
    while(ch>='0' && ch<='9') {
        x=10*x+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int main() {
    freopen("data.txt","r",stdin);
    const int Size=5000000;
    int a,b,n;
    a=clock();
    for(int i=1; i<=Size; i++) {
        n=read();
    }
    b=clock();
    printf("%d ms",b-a);
    return 0;
}

运行结果:1639ms,效率比scanfscanfscanf和关闭流输入的cin都要高很多。
推荐使用这种readreadread,代码量不大,背起来方便,考场上敲起来也不是很困难。
而且在OI中先敲一个13行的read也能给旁边的人造成压力(大雾)

优化手写read的速度

有神犇提出,readreadread还可以优化,原因是getchargetchargetchar没有freadfreadfread快!
SROSROSROSROSROSROSROSROSRO dalaodalaodalao OTZOTZOTZOTZOTZOTZOTZOTZOTZ
程序如下:

#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
inline char getc() {    //优化的getchar 
    static char buf[262145],*fs,*ft;
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}
int read() {
    int x=0,f=1;
    char ch=getc();
    while(ch<'0' || ch>'9') {
        if(ch=='-') f=-1;
        ch=getc();
    }
    while(ch>='0' && ch<='9') {
        x=10*x+ch-'0';
        ch=getc();
    }
    return x*f;
}
int main() {
    freopen("data.txt","r",stdin);
    const int Size=5000000;
    int a,b,n;
    a=clock();
    for(int i=1; i<=Size; i++) {
        n=read();
    }
    b=clock();
    printf("%d ms",b-a);
    return 0;
}

运行结果达到了487ms,是本人所知道的最快的输入方法。
不过这种东西太过玄学……

个人推荐用手写readreadread不加getc的优化

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值