关于OI比赛中scanf、cin读取数据时间比较

申明

首先,这是一个纯粹的技术讨论贴,来自群里的清华在读大神lkmcfj的测试数据,本人只是整理和简单总结。再次感谢大神熬夜肝。

目的

我们知道在OI中,常有海量数据读入需求,如何降低数据读入的时间,是一个重要的环节。因此C++党和C党对cin和scanf的争论持续不断,本人只是中立的说明一个测试。也许在不同的环境、不同的机器下,可以得到不同的测试结果。不喜可以喷。

测试环境

UBUNTU18.04 + gcc7.4.0

程序目的

从test.in读出数据,并求和。看不同的数据读入方法消耗的时间。读取的方法将包括

  • scanf
  • fread
  • fread+sscanf
  • cin快速读取

储备知识

在测试过程中,使用到了time命令。下面简单介绍一下time命令。

作用

输出的信息分别显示了该命令所花费的 real 时间、user 时间和 sys 时间。

语法

time (参数) (指令)

参数,time提供的参数。具体请参考 help。

指令,就是需要运行的命令。比如我们输入time ls,测试一下 ls 这个命令消耗的时间。

[root@localhost ~]# time ls
anaconda-ks.cfg  install.log  install.log.syslog  satools  text

real    0m0.009s
user    0m0.002s
sys     0m0.007s

输出解析

看到没有,执行时间一下子就统计出来了。但输出内容中有三个统计时间,real、user 和 sys,它们都代表什么含义呢?哪个才是 ls 命令的执行时间呢?下面我们就一起来看看这三个统计时间。

(1) real:从进程 ls 开始执行到完成所耗费的 CPU 总时间。该时间包括 ls 进程执行时实际使用的 CPU 时间,ls 进程耗费在阻塞上的时间(如等待完成 I/O 操作)和其他进程所耗费的时间(Linux 是多进程系统,ls 在执行过程中,可能会有别的进程抢占 CPU)。

(2) user:进程 ls 执行用户态代码所耗费的 CPU 时间。该时间仅指 ls 进程执行时实际使用的 CPU 时间,而不包括其他进程所使用的时间和本进程阻塞的时间。

(3) sys:进程 ls 在内核态运行所耗费的 CPU 时间,即执行内核系统调用所耗费的 CPU 时间。

现在,我们应该对这三个时间非常清楚了吧。ls 命令的真正执行时间是多少?答案就是 user+sys 的时间,但一般情况下,real=user+sys,因而我们就使用 real 的时间作为 ls 的执行时间了。

当然,这个时间结果有坑,具体我们这里不说明,不是本文讨论的范围。

测试结果

test.in文件结构

文件里第一行是 n,第二行是 n 个[ 0, 1e9]范围的整数。

运行结果

二话不说,我们先上图,再解释。

运行结果说明

第一个时间是使用scanf()函数读取test.in,并求和。消耗的时间。

第二个时间是使用fread()函数读取test.in,然后手动分析数据,求和。消耗的时间。

第三个时间是使用fread()函数读取test.in,使用sscanf()分析数据,求和。消耗的时间。

第四个时间是使用cin快读,并求和。消耗的时间。

对应的程序

使用pastebin的链接,https://paste.ubuntu.com/p/w3wKCRfTpj/。再次感谢lkmcfj做出的贡献。或者直接对应的代码如下。

数据生成程序

// generator
#include <cstdio>
#include <random>
constexpr int N = 10000000, MAXV = 1000000000;
int main()
{
    std::random_device rd;
    std::mt19937 engine(rd());
    std::uniform_int_distribution<int> genint(0, MAXV);
    printf("%d\n", N);
    for(int i = 0; i < N; ++i) printf("%d ", genint(engine));
    return 0;
}

naive scanf

// naive scanf
#include <cstdio>
int main()
{
    int n;
    scanf("%d", &n);
    long long sum = 0;
    while(n--)
    {
        int t;
        scanf("%d", &t);
        sum += t;
    }
    printf("%lld\n", sum);
    return 0;
}

fread(manual parsing)

// fread(manual parsing)
#include <cstdio>
#include <cctype>
#include <cstddef>
struct input_parser
{
    static constexpr size_t BUFFER_SIZE = 10000000;
    FILE *stream;
    char *buffer, *end_buffer, *cur;
    input_parser(FILE *s):
        stream(s), buffer(new char[BUFFER_SIZE]), cur(buffer)
    {
        size_t size = fread(buffer, 1, BUFFER_SIZE, stream);
        end_buffer = buffer + size;
    }
    ~input_parser() {delete[] buffer;}
    
    void go()
    {
        ++cur;
        if(cur == end_buffer)
        {
            size_t size = fread(buffer, 1, BUFFER_SIZE, stream);
            end_buffer = buffer + size;
            cur = buffer;
        }
    }
    template <class IntType>
    IntType get()
    {
        IntType ret = 0;
        while(!isdigit(*cur)) go();
        while(isdigit(*cur))
        {
            ret = ret * 10 + *cur - '0';
            go();
        }
        return ret;
    }
};
int main()
{
    input_parser parser(stdin);
    int n = parser.get<int>();
    long long sum = 0;
    while(n--) sum += parser.get<int>();
    printf("%lld\n", sum);
    return 0;
}

fread + sscanf

// fread + sscanf
#include <cstdio>
#include <cctype>
#include <cstddef>
struct input_parser
{
    static constexpr size_t BUFFER_SIZE = 10000000;
    FILE *stream;
    char *buffer, *end_buffer, *cur;
    input_parser(FILE *s):
        stream(s), buffer(new char[BUFFER_SIZE]), cur(buffer)
    {
        size_t size = fread(buffer, 1, BUFFER_SIZE, stream);
        end_buffer = buffer + size;
    }
    ~input_parser() {delete[] buffer;}
    
    char *next_space()
    {
        char *finder = cur;
        while(finder != end_buffer && isspace(*finder)) ++finder;
        while(finder != end_buffer && !isspace(*finder)) ++finder;
        return finder;
    }
    template <class IntType>
    IntType get() // assuming cur != end_buffer
    {
        char *nexts = next_space();
        if(nexts == end_buffer && end_buffer == buffer + BUFFER_SIZE)
        {
            size_t used_head = 0;
            while(cur != end_buffer) buffer[used_head++] = *cur++;
            size_t size = fread(buffer + used_head, 1, BUFFER_SIZE - used_head, stream);
            end_buffer = buffer + used_head + size;
            cur = buffer;
            nexts = next_space();
        }
        *nexts = 0;
        IntType t;
        sscanf(cur, "%d", &t);
        cur = nexts + 1;
        if(cur == end_buffer && end_buffer == buffer + BUFFER_SIZE)
        {
            size_t size = fread(buffer, 1, BUFFER_SIZE, stream);
            end_buffer = buffer + size;
            cur = buffer;
        }
        return t;
    }
};
int main()
{
    input_parser parser(stdin);
    int n = parser.get<int>();
    long long sum = 0;
    while(n--) sum += parser.get<int>();
    printf("%lld\n", sum);
    return 0;
}

cin快读

最后:鄙视一下大神的代码风格。开玩笑的。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值