一些优化

快读

以P10815为例。

IO 是这道大数据求和题真正的瓶颈。

数据规模最高到 \(10^8\),直接用 cin 或者 scanf 读数据,很容易卡在输入环节超时。

手写快读是比较直接的解决办法。

IO 性能模型

单次系统调用开销远高于普通算术运算。

读取总耗时

\T_{io}=T_{syscall}+T_{copy}+T_{parse} \\

\(T_{syscall}\) 代表系统调用开销,\(T_{copy}\) 是内核向用户空间拷贝数据的耗时,\(T_{parse}\) 负责字符转数字。

快读本质就是降低这三项开销。

思路

利用 fread 一次性把一大段输入拉进用户侧缓冲区,后续所有字符解析都在用户内存完成。

减少频繁进入内核的切换成本。缓冲区大小需要权衡,过小会多次读取,过大占用更多内存。

解析逻辑

遍历缓冲区内字符,跳过空格、换行这类空白分隔符。

遇到负号记录符号,依次读取数字字符并计算数值。

写解析代码时尽量精简分支,减少CPU流水线停顿。解析完成后移动指针,继续处理下一组数字。

code

复制代码
#include<cstdio>
char buf[1<<25];
int ptr;
inline int read(){
    int x=0,f=1;
    while(buf[ptr]<'0'||buf[ptr]>'9'){
        if(buf[ptr]=='-')f=-1;
        ptr++;
    }
    while(buf[ptr]>='0'&&buf[ptr]<='9'){
        x=x*10+buf[ptr]-'0';
        ptr++;
    }
    return x*f;
}
int main(){
    fread(buf,1,sizeof(buf),stdin);
    ptr=0;
    int n=read();
    long long sum=0;
    for(int i=1;i<=n;i++)sum+=read();
    printf("%lld\n",sum);
    return 4352;//🤪
}

编译优化

编译时开启 O2。

inline 函数会被内联,省去函数调用开销。

编译器会简化循环与分支,减少指令周期。

O2不会改动程序逻辑,不会破坏读入正确性。

其他

关闭同步的 cin 速度会提升,但上限不如整块缓冲快读。

scanf自带缓冲,但封装逻辑重,亿级数据下速度不足。

getchar 逐字符读取,依赖库缓冲,调用次数更多,性能弱于 fread 整块读取。

内存与缓冲区

大容量缓冲区不要放在栈里,栈空间有限,容易栈溢出。

全局变量存放在静态区,适合做大缓冲。

缓冲区尺寸选择2的幂次,方便内存对齐,提升访问速度。

边界

这种一次性预读写法,输入总量超过缓冲区容量时,单次 fread 无法读完,需要改成循环填充缓冲区。

输入格式错乱会导致指针偏移,解析结果出错。

流水线与分支优化

解析代码里的条件分支会引发分支预测失败,增加耗时。

调整判断顺序可以提升预测成功率。

无分支写法能够消除分支开销,但可读性变差,调试麻烦。