问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C++中cin/cout的性能优化和缓冲区同步问题

创作时间:
作者:
@小白创作中心

C++中cin/cout的性能优化和缓冲区同步问题

引用
CSDN
1.
https://blog.csdn.net/2301_80418172/article/details/144158786

背景导入

在C++中,标准输入输出流如 cincout 是由C++的标准库提供的;而在C语言中,scanfprintf 是由C标准库提供的。由于C++是从C发展而来的语言,C++标准库的输入输出流系统需要与C标准库的输入输出系统兼容,以确保在同一程序中能够混合使用C和C++的输入输出函数。为了实现这种兼容性,C++标准库默认会将 cincout 等C++流对象与 stdinstdout 等C标准库的流对象同步在一起。这种同步操作意味着每次使用 cincout 时,都会自动刷新C标准库的缓冲区,以确保C++和C的I/O是一致的。

在默认情况下,cincout 之间存在一种绑定关系。这种绑定意味着,每当从 cin 读取数据时,任何之前通过 cout 输出的内容都会被强制刷新到屏幕上。这个机制保证了输出内容能够立即显示给用户,这对于交互式程序非常有用。但是,这种绑定也可能导致性能问题,特别是在需要频繁读取大量数据的情况下。这是因为每次从 cin 读取数据都会触发一次输出缓冲区的刷新,即使实际上没有进行输出操作,也会浪费时间。

问题

  1. ios::sync_with_stdio(true/false) 是什么?
  2. cin/couttie(绑定关系)是什么?
  3. 为什么要解除 cin/cout 的绑定关系?
  4. 为什么要取消C++和C的缓冲区同步?

1.1 ios::sync_with_stdio(false)

我们都知道C++是从C语言发展过来的,我们还知道在 .cpp 文件中是可以同时使用C++和C的IO流的,也就是 cin/scanfcout/printf 是可以混合着用的;二者的IO是兼容的;都是共享的同一块缓冲区。

而于此相关的表示同步关系开关的代码语句就是 ios::sync_with_stdio,我们平时使用的 cin 其实是 istream 的一个对象,coutostream 的一个对象;而 ios 就是 iostream 的一个基类;sync 的意思是"同步",with_stdio 就很明显了,就是与C的IO流同步;说到这里可能还不够清楚,没关系,下面会讲到他的真正作用。

ios::sync_with_stdio 的真正作用就是在使用另外一方的IO流时刷新缓冲区。

举个例子:(这一块由于编译器的优化,无法使用代码验证)

我们都知道缓冲区是只有等到满了或者遇到回车或程序结束的时候才会自动刷新的,缓冲区刷新的表现就是将其内容打印到屏幕上。

如果我没有打开IO同步的话,那我在从C++的IO流切换到C的IO流的时候是不会强制刷新缓冲区的。

如图:我先使用 cout 了,此时在执行 scanf 之前缓冲区里是有 hello world 一个字符串的;由于我没有打开同步,所以我在执行 scanf 语句的时候屏幕上是不会打印 hello world 的;因为缓冲区没有刷新嘛。

如果我打开了IO同步,那么在执行 scanf 的时候,就会强制的把缓冲区刷新一遍,因为我要使用C的IO流了,C++在缓冲区中输入的内容要清理掉;这个时候的运行结果就会先显示 hello world,然后让你输入变量X。

由于这块编译器做了优化,所以是否解除同步,运行结果都一样,不方便验证

1.2 为什么要解除C++/C IO流同步?

正是因为每次切换IO,都要刷新一下缓冲区,如果数据量很大,程序员又经常性的切换IO,即使缓冲区没有任何内容,依旧会自动的刷新缓冲区,这就很浪费时间了。

如果我们解除了同步关系;这个缓冲区就暂时性的归属于一方;那么我们就最好不要交叉使用了;否则会造成一些不必要的麻烦。

1.3 使用场景

  • 竞赛编程:在处理大量输入输出的竞赛环境中,这种优化非常常见,因为它可以显著减少I/O操作的时间。
  • 只使用C++ I/O:如果你的程序只使用 cincout 进行I/O,而不涉及C的I/O函数,那么可以安全地使用这项优化来提高性能。

2.1 cincout 的绑定关系

前面我们讲了C++和C的IO切换会强制刷新缓冲区,这是C++的IO和C的IO的同步关系;而 cincout 也有同步关系,由于他们都是C++的IO,所以更准确的叫法是绑定。

他们之间的关系同样是作用在缓冲区上;

cin.tie(0) 是C++中用于解除标准输入流 cin 与标准输出流 cout 之间默认绑定的一个方法。在默认情况下,cincout 之间存在一种绑定关系。这种绑定意味着,每当从 cin 读取数据时,任何之前通过 cout 输出的内容都会被强制刷新到屏幕上。这个机制保证了输出内容能够立即显示给用户,这对于交互式程序非常有用。但是,这种绑定也可能导致性能问题,特别是在需要频繁读取大量数据的情况下。这是因为每次从 cin 读取数据都会触发一次输出缓冲区的刷新,即使实际上没有进行输出操作,也会浪费时间。

这里我简单举个例子:

默认状态下 cincout 是绑定了的,我使用 cout,缓冲区中存有 hello world,没有回车,缓冲区不会自动刷新。运行结果会先打印 hello world 然后在让我们输入 x 的值;

如果我们没有绑定 cincout,那执行 cin 之前就不会强制刷新缓冲区,运行结果就应该是先让你输入X,输入完回车后再打印 hello world

2.2 为什么要解除绑定关系?

这里与上面的同步解释是一样的,如果缓冲区没有内容遇到 cin,就会强制刷新缓冲区,有时完全没有必要,为了提高效率,通常采用解除绑定的方法;

如果是非交互式的程序,那就可以不解除;因为交互式程序需要给用户提示等...

如果是在ACM等竞赛中,那么往往都是需要解除绑定的;

2.3 注意事项

如果我们解除了 cin/cout 的绑定关系,cin 是不会强制刷新缓冲区的,有时候我们在输入数据之前,需要前面的提示;这个时候我们就需要使用 cout.flush 或者 cout<<endl 手动刷新缓冲区。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号