endl 并刷新缓冲区
在C++入门一书中,第(1)章中提到了以下内容:
endl 是一个特殊的值,称为操纵器,当写入 输出流具有将换行符写入输出的效果,并且 刷新与该设备关联的缓冲区。 通过刷新缓冲区,我们确保用户将看到写入到的输出 立即直播。
这里的“刷新缓冲区”是什么意思?
In the C++ primer book, in chapter (1), it mentions the following:
endl is a special value, called a manipulator, that when written to an
output stream has the effect of writing a newline to the output and
flushing the buffer associated with that device. By flushing the buffer, we ensure that the user will see the output written to the
stream immediately.
What is meant by "flushing the buffer" here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
输出通常在写入目标设备之前进行缓冲。这样,当写入慢速访问设备(如文件)时,它不必在每个字符之后访问设备。
刷新意味着清空缓冲区并将其实际写入设备。
Output is generally buffered before it's written to the intended device. That way, when writing to slow to access devices(like files), it doesn't have to access the device after every single character.
Flushing means emptying the buffer and actually writing it to the device.
C++ 的 iostream 是缓冲的,这意味着当您输出到 ostream 时,内容不会立即转到流后面的内容,例如,在 cout 的情况下为 stdout。流的实现决定了何时实际发送流的缓冲部分。这样做是出于效率的考虑,逐字节写入网络或磁盘流效率会非常低,通过缓冲这个问题就解决了。
然而,这确实意味着当您将调试消息写入日志文件并且程序崩溃时,您可能会丢失通过流写入日志文件的部分数据,因为日志的一部分可能仍然在流的缓冲区中,并且尚未写入实际文件。为了防止这种情况发生,您需要通过显式的刷新方法调用或使用 endl 的便利来使流刷新其缓冲区。
但是,如果您只是定期写入文件,则应该使用 \n 而不是 endl 来防止流不必要地刷新每行流,从而降低性能。
编辑包括此注释:
cin 和 cout 有特殊关系,从 cin 读取将自动事先刷新 cout。这可以确保在从 cin 读取等待输入之前,用户实际上可以看到您写入 cout 的提示符。因此,即使在 cout 中,通常也不需要 endl,但可以使用 \n 代替。您也可以通过将其他流绑定在一起来在其他流之间创建此类关系。
C++'s iostreams are buffered, that means that when you output to an ostream, the content will not immediately go to what is behind the stream, e.g. stdout in the case of cout. The implementation of the stream determines when to actually send the buffered part of the stream out. This is done for reasons of efficiency, it would be very inefficient to write to a network or disk stream byte by byte, by buffering this problem is solved.
This does however mean that when you write say debug messages to a log file and your program crashes you may lose part of the data you wrote to the log file through the stream, as a part of the log may still be in the stream's buffer and not yet written to the actual file. To prevent this from happening you need to make the stream flush its buffers either by an explicit flush method call, or by using the convenience of endl.
If however you're just writing to a file regularly you should use \n instead of endl to prevent the stream from unnecessarily flushing the stream every line reducing your performance.
Edited to include this note:
cin and cout have a special relationship, where reading from cin will automatically flush cout beforehand. This makes sure that the e.g. the prompt you wrote to cout will actually be seen by the user before the read from cin is waiting for input. Hence, even in cout you don't normally need endl but can use \n instead. You can create such relationships between other streams as well by tying them together.
std::endl
导致流的内部暂存内存(其“缓冲区”)中的数据“刷新”(传输)到操作系统。随后的行为取决于流映射到的设备类型,但一般来说,刷新会显示数据已物理传输到关联设备。然而,突然断电可能会打破这种幻想。这种刷新涉及一些开销(浪费时间),因此在执行时应尽量减少速度是一个重要的问题。最大限度地减少这种开销的总体影响是数据缓冲的根本目的,但这个目标可能会因过度冲水而落空。
背景信息
计算系统的 I/O 通常非常复杂并且由多个抽象层组成。每个这样的层都可能引入一定量的开销。数据缓冲是通过最小化系统两层之间执行的单个事务的数量来减少这种开销的一种方法。
CPU/内存系统级缓冲(缓存):对于非常高的活动,甚至计算机的随机存取内存系统也可能成为瓶颈。为了解决这个问题,CPU 通过提供多层隐藏缓存(其中的各个缓冲区称为缓存行)来虚拟化内存访问。这些处理器缓存缓冲算法的内存写入(根据写入策略)为了最大限度地减少内存总线上的冗余访问。
应用程序级缓冲:虽然并不总是必要,但应用程序在将输出数据传递到 I/O 库之前分配内存块来累积输出数据的情况并不少见。这提供了允许随机访问(如果有必要)的基本好处,但这样做的一个重要原因是它最大限度地减少了与进行库调用相关的开销——这可能比简单地写入内存阵列更耗时.
I/O 库缓冲:C++ IO 流库 a> 可以选择为每个打开的流管理一个缓冲区。该缓冲区特别用于限制系统调用 到操作系统内核,因为此类调用往往会产生一些不小的开销。 这是使用
std::endl
时刷新的缓冲区。操作系统内核和设备驱动程序:操作系统路由根据流附加到的输出设备将数据传输到特定的设备驱动程序(或子系统)。此时,实际行为可能会根据该类型设备的性质和特征而有很大差异。例如,当设备是硬盘时,设备驱动程序可能不会立即启动到设备的传输,而是维护自己的缓冲区以进一步减少冗余操作(因为磁盘也是如此)以块的形式写入是最有效的)。为了显式刷新内核级缓冲区,可能需要调用系统级函数,例如 Linux 上的
fsync()
——甚至关闭 关联的流,不一定强制进行此类刷新。示例输出设备可能包括...
硬件缓冲区:特定硬件可能包含自己的内存缓冲区。例如,硬盘驱动器通常包含一个磁盘缓冲区,以便(除其他外)允许物理写入的发生不需要系统的CPU参与整个过程。
在许多情况下,这些不同的缓冲层往往(在某种程度上)是多余的——因此基本上是多余的。然而,如果其他层由于某种原因无法针对与每层相关的开销提供最佳缓冲,则每层的缓冲可以提供巨大的吞吐量增益。
长话短说,
std::endl
仅解决了由该特定流的 C++ IO 流库管理的缓冲区。调用 std::endl 后,数据将被移至内核级管理,数据接下来会发生什么取决于很多因素。如何避免
std::endl
的开销std::endl
- 使用'\n'
代替。std::endl
-- 使用某些东西就像下面的版本...在此示例中,您提供了一个自定义
endl
,可以通过或不调用对flush()
(这就是强制传输到操作系统的原因)。启用刷新(使用debug_mode
变量)对于调试场景很有用,在这种情况下,您希望能够在程序终止时在完全关闭关联流之前检查输出(例如磁盘文件) (这将强制最后刷新缓冲区)。std::endl
causes the data in the stream's internal staging memory (its "buffer") to be "flushed" (transferred) to the operating system. The subsequent behavior depends on what type of device the stream is mapped to, but in general, flushing will give the appearance that the data has been physically transferred to the associated device. A sudden loss of power, however, might defeat the illusion.This flushing involves some overhead (wasted time), and should therefore be minimized when execution speed is an important concern. Minimizing the overall impact of this overhead is the fundamental purpose of data buffering, but this goal can be defeated by excessive flushing.
Background information
The I/O of a computing system is typically very sophisticated and composed of multiple abstraction layers. Each such layer may introduce a certain amount of overhead. Data buffering is a way of reducing this overhead by minimizing the number of individual transactions performed between two layers of the system.
CPU/memory system-level buffering (caching): For very high activity, even the random-access-memory system of a computer can become a bottleneck. To address this, the CPU virtualizes memory accesses by providing multilple layers of hidden caches (the individual buffers of which are called cache lines). These processor caches buffer your algorithm's memory writes (pursuant to a writing policy) in order to minimize redundant accesses on the memory bus.
Application-level buffering: Although it isn't always necessary, it is not uncommon for an application to allocate chunks of memory to accumulate output data before passing it to the I/O library. This provides the fundamental benefit of allowing for random accesses (if necessary), but a significant reason for doing this is that it minimizes the overhead associated with making library calls -- which may be substantially more time-consuming than simply writing to a memory array.
I/O library buffering: The C++ IO stream library optionally manages a buffer for every open stream. This buffer is used, in particular, to limit the number of system calls to the operating system kernel because such calls tend to have some non-trivial overhead. This is the buffer which is flushed when using
std::endl
.operating system kernel and device drivers: The operating system routes the data to a specific device driver (or subsystem) based on what output device the stream is attached to. At this point, the actual behavior may vary widely depending on the nature and characteristics of that type of device. For example, when the device is a hard disk, the device driver might not initiate an immediate transfer to the device, but rather maintain its own buffer in order to further minimize redundant operations (since disks, too, are most efficiently written to in chunks). In order to explicitly flush kernel-level buffers, it may be necessary to call a system-level function such as
fsync() on Linux
-- even closing the associated stream, doesn't necessarily force such flush.Example output devices might include...
hardware buffers: Specific hardware may contain its own memory buffers. Hard drives, for example, typically contain a disk buffer in order to (among other things) allow the physical writes to occur without requiring the system's CPU to be engaged in the entire process.
Under many circumstances, these various buffering layers tend to be (to a certain extent) redundant -- and therefore essentially overkill. However, the buffering at each layer can provide a tremendous gain in throughput if the other layers, for whatever reason, fail to deliver optimum buffering with respect to the overhead associated with each layer.
Long story short,
std::endl
only addressed the buffer which is managed by the C++ IO stream library for that particular stream. After callingstd::endl
, the data will have been moved to kernel-level management, and what happens next with the data depends on a great many factors.How to avoid the overhead of
std::endl
std::endl
-- use'\n'
instead.std::endl
-- use something like the following version instead...In this example, you provide a custom
endl
which can be called with-or-without invoking the internal call toflush()
(which is what forces the transfer to the operating system). Enabling the flush (with thedebug_mode
variable) is useful for debugging scenarios where you want to be able to examine the output (for example a disk-file) when the program has terminated before cleanly closing the associated streams (which would have forced a final flush of the buffer).使用
std::cout
时,输出运算符 (<<
) 之后使用的操作数存储在缓冲区中,并且不会显示在stdin< 上/code> (通常是终端或命令提示符),直到遇到
std::endl
或std::cin
,这会导致缓冲区被刷新 ,从某种意义上说,将缓冲区的内容显示/输出到stdin
上。考虑这个程序:
获得的输出将是:
2 秒后
Hello, World
When using
std::cout
, the operand used after the output operator (<<
) are stored in a buffer and are not displayed onto thestdin
(usually terminal, or the command prompt) until it comes acrossstd::endl
orstd::cin
, which causes the buffer to be flushed, in the sense, display/output the contents of the buffer onto thestdin
.Consider this program:
The output obtained will be:
after 2 seconds
Hello, World
一段简单的代码向您展示 C++ 中缓冲 I/O 的效果
无论您提供什么输入,都会被缓冲,然后在有输入的情况下传递给程序变量。
看一下下面的代码:
这里我们声明了两个变量,一个 int 和一个 char
如果我们输入数字“12d34”
这将导致 int 变量只接受 12 作为值,并且它将丢弃仍在缓冲区中的其余部分。
在下一个输入中,char变量将自动接受值“d”
甚至不需要您提供任何意见
One simple code to show you the effects of buffered I/O in c++
Whatever input you provide is buffered and then passed on to the program variables in case of inputs.
Have a look at the code below:
here we have declared two variables one int and one char
if we input the number as "12d34"
this will cause the int variable to accept only 12 as value and it will discard the rest which will still be there in the buffer.
And in the next input the char variable will automatically accept the value "d"
without even asking you for any input