简体   繁体   English

结束并冲洗缓冲区

[英]endl and flushing the buffer

In the C++ primer book, in chapter (1), it mentions the following: C ++入门书籍的第(1)章中,它提到了以下内容:

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 . endl是一个特殊的值,称为操纵器,它在写入输出流时具有将换行符写入输出并刷新与该设备关联的缓冲区的作用 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? 这里的“刷新缓冲区”是什么意思?

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++'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, eg stdout in the case of cout. C ++的iostream被缓冲,这意味着当您输出到ostream时,内容不会立即转到该流的后面,例如,在cout的情况下为stdout。 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. 为了防止这种情况的发生,您需要通过显式的flush方法调用或使用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. 但是,如果您只是定期写入文件,则应使用\\ n而不是endl来防止流不必要地刷新每一行,从而降低性能。

Edited to include this note: 编辑以包括此注释:

cin and cout have a special relationship, where reading from cin will automatically flush cout beforehand. cin和cout有特殊的关系,从cin读取将自动预先清除cout。 This makes sure that the eg the prompt you wrote to cout will actually be seen by the user before the read from cin is waiting for input. 这样可以确保在从cin读取等待输入之前,用户实际看到了您写入cout的提示。 Hence, even in cout you don't normally need endl but can use \\n instead. 因此,即使在cout中,您通常也不需要endl,而可以使用\\ n代替。 You can create such relationships between other streams as well by tying them together. 您也可以通过将其他流绑定在一起来创建此类关系。

What is meant by "flushing the buffer" here? 这里的“刷新缓冲区”是什么意思?

std::endl causes the data in the stream's internal staging memory (its "buffer") to be "flushed" (transferred) to the operating system. std::endl导致流的内部登台内存(其“缓冲区”)中的数据被“刷新”(传输)到操作系统。 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. 计算系统的I / O通常非常复杂,由多个抽象层组成。 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. CPU /内存系统级缓冲(缓存) :对于非常高的活动,甚至计算机的随机访问内存系统也可能成为瓶颈。 To address this, the CPU virtualizes memory accesses by providing multilple layers of hidden caches (the individual buffers of which are called cache lines). 为了解决这个问题,CPU通过提供多层隐藏的高速缓存(其各个缓冲区称为高速缓存行)来虚拟化内存访问。 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. 应用程序级缓冲 :尽管并非总是必要,但在传递给I / O库之前,应用程序分配内存块以累积输出数据并不罕见。 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. I / O库缓冲C ++ IO流库可以选择管理每个打开的流的缓冲区。 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 . 这是使用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. 为了显式刷新内核级缓冲区,可能有必要fsync() on Linux诸如fsync() on Linux类的系统级函数-即使关闭关联的流,也不一定强制执行此类刷新。

    Example output devices might include... 示例输出设备可能包括...

    • a terminal on the local machine 本地计算机上的终端
    • a terminal on a remote machine (via SSH or similar) 远程计算机上的终端(通过SSH或类似工具)
    • data being sent to another application via pipes or sockets 数据通过管道或套接字发送到另一个应用程序
    • many variations of mass-storage devices and associated file-systems, which may be (again) locally attached or distributed via a network 大容量存储设备和相关文件系统的许多变体,它们可能(再次)通过网络本地连接或分布
  • 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. 例如,硬盘驱动器通常包含一个磁盘缓冲区 ,以便(除其他事项外)允许进行物理写操作,而不需要系统的CPU参与整个过程。

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. 长话短说, std::endl 解决了由C ++ IO流库针对该特定流管理的缓冲区。 After calling std::endl , the data will have been moved to kernel-level management, and what happens next with the data depends on a great many factors. 调用std::endl ,数据将被移至内核级管理,接下来该如何处理数据取决于很多因素。


How to avoid the overhead of std::endl 如何避免std::endl的开销


inline std::ostream & endl( std::ostream & os )
   {
   os.put( os.widen('\n') ); // http://en.cppreference.com/w/cpp/io/manip/endl
   if ( debug_mode ) os.flush(); // supply 'debug_mode' however you want
   return os;
   }

In this example, you provide a custom endl which can be called with-or-without invoking the internal call to flush() (which is what forces the transfer to the operating system). 在此示例中,您提供了一个自定义的endl ,可以调用或不调用内部调用flush()来调用它(这是强制将其转移到操作系统的原因)。 Enabling the flush (with the debug_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). 启用刷新(使用debug_mode变量)对于调试情况非常有用,在这种情况下,您希望能够在程序彻底关闭关联的流之前终止之前检查输出(例如磁盘文件)(这将迫使最终刷新缓冲区)。

When using std::cout , the operand used after the output operator ( << ) are stored in a buffer and are not displayed onto the stdin (usually terminal, or the command prompt) until it comes across std::endl or std::cin , which causes the buffer to be flushed , in the sense, display/output the contents of the buffer onto the stdin . 当使用std::cout ,在输出运算符( << )之后使用的操作数存储在缓冲区中,并且直到遇到std::endlstd::cin时才显示在stdin (通常是终端或命令提示符)上std::cin ,从某种意义上说,这会使缓冲区被刷新 ,将缓冲区的内容显示/输出到stdin

Consider this program: 考虑以下程序:

#include <iostream>
#include <unistd.h>

int main(void)
{
    std::cout << "Hello, world";
    sleep(2);
    std::cout << std::endl;

    return 0;
}

The output obtained will be: 获得的输出将是:

after 2 seconds 2秒后

Hello, World 你好,世界

One simple code to show you the effects of buffered I/O in c++ 一个简单的代码向您展示c ++中缓冲I / O的影响

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: 看下面的代码:

//program to test how buffered  I/O can have unintended effects on our program

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int a;
    char c;
    cin>>a;
    cin>>c;
    cout<<"the number is : "<<a;
    cout<<"\nthe character is : "<<c;
}

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. 在这里我们声明了两个变量,一个int和一个char,如果我们将数字输入为“ 12d34”,这将导致int变量仅接受12作为值,并且将丢弃仍保留在缓冲区中的其余变量。 And in the next input the char variable will automatically accept the value "d" without even asking you for any input 在下一个输入中,char变量将自动接受值“ d”,甚至不要求您进行任何输入

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM