在 std::cout 和 std::endl 的上下文中使用输出流缓冲区
Usage of output stream buffer in context to std::cout and std::endl
我正在尝试了解输出流缓冲区的工作原理。我没有找到任何内容来解释在 std::cout
的上下文中编写标准输出的整个过程。据我了解, std::cout
实际上并没有在标准输出上打印,而是将内容写入终端仿真器的输出流缓冲区。当缓冲区已满时,终端仿真器删除缓冲区的内容并将其写入终端显示(标准输出)。根据我的说法,这就是终端仿真器执行 flush
操作时发生的情况。
在 std::endl
的情况下,向终端仿真器发出明确请求以立即刷新缓冲区的内容,这会导致性能下降。这种下降是由于我们的程序需要时间来创建对终端仿真器的 flush
请求并等待缓冲区的内容在继续下一行代码之前打印在终端显示器上。没有 std::endl
,std::cout
不关心立即打印内容。它将打印的责任留给终端仿真器(通过将内容写入终端仿真器的输出流缓冲区)。我有几点疑惑:
(1) 我对终端仿真器如何显示 std::cout
请求的文本的理解准确吗?
(2) flush
请求是向 OS 还是终端仿真器发出的?
(3) 如果我们增加终端仿真器的输出流缓冲区大小,是否会提高程序的性能,同时在显示器上打印内容时出现明显的延迟?
不对,你的理解不正确。 "Terminal emulators" 没有参与,至少没有直接参与。
输出缓冲区存在于流对象本身中(在本例中为std::cout
)。根据流的缓冲策略,可以使用三种可能的方式使用此缓冲区:
块缓冲:输出首先存储在缓冲区中。当缓冲区已满时,它是 "flushed",即它的内容被写入底层(OS 特定)输出通道并且缓冲区被清空。
缓冲行:工作方式类似于缓冲块,但每当向其中写入换行符 ('\n'
) 时也会刷新缓冲区。
Unbuffered:不使用输出缓冲区。所有输出立即写入。
当您打开文件时,流开始被块缓冲。 std::cerr
是无缓冲的。 std::cout
如果输出到终端则行缓冲,否则块缓冲。
std::flush
立即刷新输出缓冲区(如果有的话)。
至于数据的实际写入方式,具体取决于您的操作系统。在 unix 系统上,有一个名为 write
的系统调用("system call" 是对操作系统执行某些操作的请求)。系统调用通常比普通函数调用慢;输出缓冲区是一种性能优化,因为您不想为输出的每个字符调用 write
。在内部收集输出,直到您有更多的文本要写入意味着更少的调用 write
,这意味着更好的整体性能。
关于您的具体问题:
不,终端仿真器不相关。
OS.
缓冲区在流对象中,而不是终端仿真器中。增加缓冲区大小会在某个时间点后停止为您提供任何性能优势。您的程序通常会将大部分时间花在计算结果和执行除向 std::cout
.
写入文本之外的其他事情上
我正在尝试了解输出流缓冲区的工作原理。我没有找到任何内容来解释在 std::cout
的上下文中编写标准输出的整个过程。据我了解, std::cout
实际上并没有在标准输出上打印,而是将内容写入终端仿真器的输出流缓冲区。当缓冲区已满时,终端仿真器删除缓冲区的内容并将其写入终端显示(标准输出)。根据我的说法,这就是终端仿真器执行 flush
操作时发生的情况。
在 std::endl
的情况下,向终端仿真器发出明确请求以立即刷新缓冲区的内容,这会导致性能下降。这种下降是由于我们的程序需要时间来创建对终端仿真器的 flush
请求并等待缓冲区的内容在继续下一行代码之前打印在终端显示器上。没有 std::endl
,std::cout
不关心立即打印内容。它将打印的责任留给终端仿真器(通过将内容写入终端仿真器的输出流缓冲区)。我有几点疑惑:
(1) 我对终端仿真器如何显示 std::cout
请求的文本的理解准确吗?
(2) flush
请求是向 OS 还是终端仿真器发出的?
(3) 如果我们增加终端仿真器的输出流缓冲区大小,是否会提高程序的性能,同时在显示器上打印内容时出现明显的延迟?
不对,你的理解不正确。 "Terminal emulators" 没有参与,至少没有直接参与。
输出缓冲区存在于流对象本身中(在本例中为std::cout
)。根据流的缓冲策略,可以使用三种可能的方式使用此缓冲区:
块缓冲:输出首先存储在缓冲区中。当缓冲区已满时,它是 "flushed",即它的内容被写入底层(OS 特定)输出通道并且缓冲区被清空。
缓冲行:工作方式类似于缓冲块,但每当向其中写入换行符 (
'\n'
) 时也会刷新缓冲区。Unbuffered:不使用输出缓冲区。所有输出立即写入。
当您打开文件时,流开始被块缓冲。 std::cerr
是无缓冲的。 std::cout
如果输出到终端则行缓冲,否则块缓冲。
std::flush
立即刷新输出缓冲区(如果有的话)。
至于数据的实际写入方式,具体取决于您的操作系统。在 unix 系统上,有一个名为 write
的系统调用("system call" 是对操作系统执行某些操作的请求)。系统调用通常比普通函数调用慢;输出缓冲区是一种性能优化,因为您不想为输出的每个字符调用 write
。在内部收集输出,直到您有更多的文本要写入意味着更少的调用 write
,这意味着更好的整体性能。
关于您的具体问题:
不,终端仿真器不相关。
OS.
缓冲区在流对象中,而不是终端仿真器中。增加缓冲区大小会在某个时间点后停止为您提供任何性能优势。您的程序通常会将大部分时间花在计算结果和执行除向
std::cout
. 写入文本之外的其他事情上