2

我正在阅读加速 C++,作者写道:

当您编写可能需要很长时间运行的程序时,在适当的时候刷新输出缓冲区是一个重要的习惯。否则,程序的某些输出可能会在您的程序编写它和您看到它之间的很长一段时间内在系统缓冲区中停滞不前

如果我误解了这些概念中的任何一个,请纠正我:

  • 缓冲区:一块随机存取存储器,用于临时保存输入或输出。
  • 冲洗:释放已分配给某些 ..umm 的随机存取内存。

我找到了这个解释:

刷新输出设备意味着需要立即完成所有前面的输出操作。这与缓冲问题有关,缓冲是操作系统使用的一种优化技术。粗略地说,操作系统保留(并且通常行使)将数据“待机”的权利,直到它决定它有足够大的数据量来证明将数据发送到屏幕的相关成本是合理的。然而,在某些情况下,我们需要保证程序中执行的输出操作在程序执行的给定点完成,因此我们刷新输出设备。

继续那个解释,我读到导致系统刷新缓冲区的三个事件:

  1. 缓冲区已满,将自动刷新
  2. 可能会要求库从标准输入流中读取 *是标准输入流,例如std::cin >> name ;
  3. 第三种情况是我们明确告诉它。我们如何明确地告诉它?

尽管我不觉得完全掌握以下内容:

  • 什么是输出缓冲区与只是一个缓冲区和可能的其他类型的缓冲区......
  • 刷新缓冲区意味着什么。它只是意味着清除内存吗?
  • 上面解释中提到的“输出设备”是什么
  • 最后,在这一切之后,什么时候是冲洗缓冲区的好时机……嗯,这听起来并不愉快。
4

5 回答 5

1

要冲洗std::ostream,请使用std::flush操纵器。IE

std::cout << std::flush;

请注意,std::endl已经刷新了流。因此,如果您习惯于以它结束插入,则无需执行任何其他操作。请注意,这意味着如果由于刷新过多而导致性能不佳,则需要从插入切换std::endl到插入换行符:'\n'

流是字符序列(即类型的事物char)。输出流是您写入字符的流。典型的应用程序是将数据写入文件、在屏幕上打印文本或将它们存储在std::string.

流通常具有这样的特性,即一次写入 1024 个字符比一次写入 1 个字符 1024 次要快一个数量级(或更多!)。“缓冲”概念的主要目的之一是以方便的方式处理这个问题。与其直接写入您真正希望字符去的任何内容,不如写入缓冲区。然后,当您准备好时,您“刷新”缓冲区:将字符从缓冲区移动到您想要的位置。或者,如果您不关心精确的细节,您可以使用自动刷新自身的缓冲区。例如,在 an 中使用的缓冲区std::ofstream通常是固定大小的,并且会在其满时刷新。

你问什么时候是冲洗的好时机?我说你过早地优化。:) 与其寻找完美的冲洗时间,不如经常这样做。进行足够多的刷新,以便足够频繁地刷新,以至于您永远不会发现自己处于例如您想要查看文件中的数据但它未写入缓冲区中的情况。然后,如果确实有太多的冲洗会损害性能,那就是你花时间研究它的时候。

于 2012-05-30T07:53:06.500 回答
0

我认为作者的意思是流缓冲区。刷新缓冲区的好时机实际上取决于您的代码做什么,它的构造方式和缓冲区的分配方式,以及它的初始化范围。

对于流和输出缓冲区,看看这个

是的,标准输入流意味着使用 >> 运算符。(大多)

您可以通过调用来明确告诉流缓冲区刷新ofstream::flush,当然其他类型的缓冲区有自己的显式刷新方法,有些可能需要手动实现。

于 2012-05-30T06:08:36.303 回答
0

您使用 . 显式刷新流your_stream.flush();

什么是输出缓冲区与只是一个缓冲区和可能的其他类型的缓冲区......

缓冲区通常是一块内存,用于保存等待处理的数据。一种典型的用途是刚刚从流中读取的数据,或等待写入磁盘的数据。无论哪种方式,一次读取/写入大块数据通常更有效,因此一次读取/写入整个缓冲区,但客户端代码可以方便地读取/写入任何数量(例如,一个字符或一个一次一行)。

刷新缓冲区意味着什么。它只是意味着清除内存吗?

那要看。对于输入缓冲区,是的,这通常意味着只清除缓冲区的内容,丢弃已读入缓冲区的任何数据(尽管它通常不会清除 RAM ——它只是将其内部簿记设置为缓冲区为空)。

对于输出缓冲区,刷新缓冲区通常意味着强制将缓冲区中的任何数据立即写入关联的流。

上面解释中提到的“输出设备”是什么

当您写入数据时,它是您最终写入的任何设备。这可能是磁盘、屏幕等上的文件。

最后,在这一切之后,什么时候是冲洗缓冲区的好时机……嗯,这听起来并不愉快。

当您完成一段时间的数据写入时,一个明显的合适时机是正确的,并且您将返回一段时间不产生任何输出(至少到同一目的地)的处理(或其他)。如果您之后可能会产生更多数据到同一个地方,您不希望刷新缓冲区 - 但您也不希望将数据留在缓冲区中,因为在您之前会有明显的延迟填充缓冲区(或其他),以便将数据写入其目的地。

于 2012-05-30T06:21:05.547 回答
0

这在很大程度上取决于应用程序的类型,但一条经验法则是在写入一条记录后刷新。对于通常在每一行之后的文本,对于每个对象之后的二进制数据。如果性能似乎很慢,则刷新您写入的每条 X 记录,并尝试使用 X,直到您对性能感到满意时找到一个数字,而 X 不够大,因此您会丢失太多数据,以防万一崩溃。

于 2012-05-30T05:58:59.367 回答
0

一一回答你的问题:

  • 通常,缓冲区只是用于临时保存数据的一块内存。当写入 `std::ofstream` 时,字符被发送到 `std::filebuf`,默认情况下,通常会将它们简单地放入缓冲区而不是立即输出到系统。当使用 `std::ofstream` 时,实际上有两个缓冲区在运行,一个在 `ofstream` 中(在您的进程中),一个在操作系统中。
  • 该标准将底层数据称为外部支持上的字符序列,缓冲区表示该序列的窗口;输出数据可能只更新缓冲区中的图像,刷新将缓冲区中的图像与操作系统拥有的数据图像“同步”。如果您要输出到真实文件,这是一个相当好的描述,但如果您直接输出到串行端口或类似的东西,操作系统不维护任何“图像”数据。基本上,如果您已将数据写入尚未传输到操作系统的流中,则刷新缓冲区会将其传输到操作系统(这意味着 `ofstream` 可以重用缓冲内存以进行进一步缓冲)。通常冲洗缓冲液(即 在我知道的所有实现上)只与操作系统同步(这是标准所要求的);它不能确保数据已实际写入磁盘。根据应用程序,这可能是也可能不是问题。
  • “输出设备”是系统想要的任何东西。一个文件、屏幕上的一个窗口,或者在旧时代或更简单的系统、打印机或串行端口上。而且您引用的解释非常具有误导性(或者更确切地说不是在谈论“ofstream”),因为刷新“ofstream”并不能确保所有前面的输出操作都已完全完成。它只确保流缓冲区中的数据已传输到(与)操作系统同步。在大多数情况下(至少在 Windows 和 Unix 下),这意味着数据已从一个缓冲区(在您的进程中)移动到另一个缓冲区(在操作系统中)。
  • 合适的时机在很大程度上取决于应用程序正在做什么。作为一般规则,我建议经常刷新,这样如果您的程序崩溃,您可以或多或少地看到它已经走了多远。(请记住,输出 `std::endl` 刷新。对于大多数简单的使用,只使用 `std::endl` 而不是 `'\n'` 就足够了。)至少有两种情况需要您考虑更多然而,关于冲洗;如果您在一个块中输出大量数据(即在输出之间只进行格式化),过度刷新会大大减慢输出速度。在这种情况下,您可能需要考虑使用 `'\n'` 而不是 `std::endl`。另一个是用于日志记录之类的事情,您希望数据立即出现,

std::ostream::flush()如果您调用或 ,数据将被显式刷新std::ofstream::close()。(当然,在后一种情况下,您以后不能写入更多数据。)

还要注意,由于数据在刷新之前实际上并未“写入”,因此在此之前无法检测到大多数可能的错误。特别是,类似:

if ( output << data ) {
    //  succeeded...
}

实际上不起作用;报告的“成功”ofstream只是它已成功将字符复制到其缓冲区中(几乎不会失败)。

写入大块数据时,通常的习惯用法是不中断地写入,不刷新,然后关闭文件并检查错误。如果您希望数据立即出现,那么在中断写入时这是不合适的,并且它的缺点是如果您的程序崩溃,您“写入”的一些数据将会消失,这会使调试变得更加困难。

于 2012-05-30T08:19:37.463 回答