一、前言

之前写题遇到大数据量(cin、cout 数据量级达到 1e5、1e6 ),因为考虑 IO 性能报错 TLE,故选择 scanf、printf 替代 cin、cout,以解决问题。一直以来没有深入研究其中原因,只知关键词——同步,虽是本质但差之千里,今究其因,记录本文。

针对上述场景无法 AC,起因是打 LC 周赛发现 top 选手用的下述代码:

// 已做相关改进,包括 cin.tie(0) 被替换为 cin.tie(nullptr),删掉不必要的 cout.tie(0),因不涉及本文主题故不详细展开
using namespace std;
ios::sync_with_stdio(false);
cin.tie(nullptr);

题目

(以本题最短路,报错 TLE 为例——附题解传送)


二、ios::sync_with_stdio(false)

在调用 ios::sync_with_stdio(false) 后,cout 与 stdout 不再共享同一块缓冲区,它们分别管理自己的缓冲区。简述,函数作用为设置标准 C++ 流是否与标准 C 流在每次输入/输出操作后同步(官方文档,见下图)。

正是因为这种同步,所以 cin、cout 比 scanf、printf 速度要慢,如果我们在使用 cin、cout 输入输出前加一句 ios::sync_with_stdio(false),即取消缓冲区同步,可节省时间,效率与 scanf、printf 相差无几。

具体功能,需要注意以下几点:

1、实践中,这表示同步的 C++ 流为无缓冲,而每次 C++ 流上的 I/O 都立即应用到对应 C 流的缓冲区。这使得能自由地混合 C++ 与 C I/O 。
2、同步的 C++ 流保证为线程安全(从多个线程输出的单独字符可能交错,但无数据竞争)。
3、若关闭同步,则允许 C++ 标准流独立地缓冲其 I/O ,可认为这在某些情况下更快。
4、所有八个标准 C++ 流默认与其相应的 C 流同步。
5、若在标准流上已出现 I/O 后调用此函数,则行为是实现定义的:有的实现无效果,有的实现销毁读取缓冲区。


三、cin.tie(nullptr)

tie 是一个函数,将两个 stream 绑定,空指针的话返回当前输出流指针(官方文档,见下图)。

函数介绍(注意返回值):

std::basic_ostream<CharT,Traits>* tie() const; // 返回当前联系流。若无联系流,则返回空指针。
std::basic_ostream<CharT,Traits>* tie( std::basic_ostream<CharT,Traits>* str ); // 设置当前联系流为 str ,返回操作前的联系流。若无联系流,则返回空指针。

cin 默认是与 cout 绑定,所以每次 cin 操作的时候都在联系流(即输出流)调用 flush(),这样增加了 IO 负担,通过 cin.tie(nullptr) 来解除 cin 和 cout 之间的绑定,进一步加快执行效率。同时,解除绑定带来的效果,详述如下:

cin 默认绑定了 cout 来同步在控制台输出。“绑定”的效果,每当被“绑定”的对象有出入或输出操作,就会即时刷新(本质,在一定刷新频率下刷新)“绑定”的对象的缓冲区,以达到即时回显的效果。cout 没有默认绑定其他输出,所以 cout.tie() 获取到空指针。

代码验证:

#include <iostream>
using namespace std;

int main() {
    cout << "cin.tie(): " << cin.tie() << endl;
    cout << "cin.tie(nullptr): " << cin.tie(nullptr) << endl; // 返回操作前的联系流
    cout << "cin.tie(): " << cin.tie() << endl;
    cout << endl;
    cout << "cerr.tie(): " << cerr.tie() << endl;
    cout << "cerr.tie(nullptr): " << cerr.tie(nullptr) << endl; // 返回操作前的联系流
    cout << "cin.tie(): " << cin.tie() << endl;
    cout << endl;
    cout << "clog.tie(): " << clog.tie() << endl;
    cout << "cout.tie(): " << cout.tie() << endl;
    return 0;
}

输出:

cin.tie(): 0x55dea3046140
cin.tie(nullptr): 0x55dea3046140
cin.tie(): 0

cerr.tie(): 0x55dea3046140
cerr.tie(nullptr): 0x55dea3046140
cin.tie(): 0

clog.tie(): 0
cout.tie(): 0

四、小结

对文章内容有不解,请随时留言。


Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐