使用ofstream进行缓冲文本输出以获得性能

时间:2012-02-24 04:29:50

标签: c++ performance file-io

我需要编写一个程序,它会在输出文件中写入许多字符。 我的程序还需要编写换行符以便更好地格式化。 我理解ofstream是一个缓冲流,如果我们为文件io使用缓冲流,我们就会获得性能。但是,如果我们使用std::endl,则会刷新输出,并且由于缓冲输出,我们将失去任何潜在的性能增益。

我想如果我将'\n'用于新行,则只有在std::endl时才会刷新输出。它是否正确?是否有任何技巧可用于在文件输出期间获得性能增益?

注意:我想在文件写入操作完成时刷新缓冲输出。我认为通过这种方式,我可以最小化文件I / O,从而获得性能。

3 个答案:

答案 0 :(得分:18)

通常,如果需要最大性能,流类的用户不应该弄乱流的刷新:当流满时,流会在内部刷新缓冲区。这实际上比等待所有输出都准备好更有效,特别是对于大文件:缓冲数据在仍然可能在内存中时写入。如果您创建一个巨大的缓冲区,并且只有在虚拟内存系统将部分数据放入光盘而不是文件时才写入。需要从光盘读取并再次写入。

std::endl相关的要点是,人们滥用它会导致缓冲区刷新,并且他们不知道性能影响。 std::endl的意图是人们可以控制在合理的点上刷新文件。为了使其有效,他们需要知道他们在做什么。可悲的是,有太多的人不知道std::endl做了什么宣传它作为一个行结尾的用途,以至于在很多地方使用它是完全错误的。

也就是说,以下是您可能希望尝试提高性能的一些事项。我假设你需要格式化输出(使用std::ofstream::write()不会给你)。

  • 显然,除非必须,否则请勿使用std::endl。如果编写代码已经存在并且在许多地方使用std::endl,其中一些可能在您无法控制的地方,则可以使用过滤流缓冲区,该缓冲区使用其大小合理的内部缓冲区,并且不会将调用转发给其{ {1}}函数到底层流缓冲区。虽然这涉及一个额外的副本,但这比一些虚假的冲洗更好,因为这些是更高的数量级。
  • 虽然它不会对sync()产生影响,但调用std::ofstream曾经影响某些实现的性能。如果这会产生影响,您可能希望使用不同的IOstream实现,因为在性能方面可能存在更多错误。
  • 确保您在std::ios_base::sync_with_stdio(false)调用std::locale时使用std::codecvt<...>返回true的{​​{1}}。使用always_noconv()可以轻松检查这一点。您可以使用std::use_facet<std::codecvt<char, char, stdd::mbstate_t> >(out.get_loc()).always_noconv()来获取std::locale("C"),这应该是真的。
  • 某些语言环境实现使用非常低效的数字方面实现,即使它们相当不错,std::locale方面的默认实现仍可能执行您不需要的操作。特别是如果您的数字格式相当简单,即您没有继续更改格式标记,您没有替换字符映射(即您不使用有趣的std::num_put<char>方面)等等。这可能是合理的使用自定义std::ctype<char>方面:为整数类型创建一个快速但简单的格式化函数并为内部不使用std::num_put<char>的浮点创建一个良好的格式化函数相当容易。

有些人建议使用内存映射文件,但这只有在预先知道目标文件的大小时才有效。如果是这种情况,这是一种提高性能的好方法,否则就不值得了。请注意,您可以通过创建使用内存映射接口的自定义snprintf()来将流格式化与内存映射文件(或更常见地,使用任何类型的输出接口)一起使用。我发现内存映射在与std::streambuf s一起使用时有时会有效。在许多情况下,差异并不重要。

很久以前我编写了自己的IOStreams和locales实现,它没有遇到上面提到的一些性能问题(可以从my site获得,但它有点陈旧,我还没有触及它已经近10年了)。有很多事情可以通过这个实现来改进,但我没有最新的实现,我准备在某处发布。很快,希望 - 这是我近10年来一直在想的事情,但是......

答案 1 :(得分:3)

打印\n时不会(必须)刷新输出,而打印std::endlstd::flush则会。

如果你想要快速写作并且在完成任务之前不关心数据是否存在,那么用\n完成所有写作并且不要担心(从{{3}开始)也将刷新流。)

如果您仍未获得所需的性能,可以使用closing the file - 它可以让您以任何大小的块读取数据(尝试更大的块,看看是否有帮助)。

答案 2 :(得分:0)

是的,endl会刷新流。不要将它用于大文件。

还要确保设置 流缓冲区 。当没有设置缓冲区时,至少MSVC实现将 1个字符一次复制到filebuf(参见streambuf::xsputn)。这可能会使您的应用程序受CPU限制,从而导致I / O速率降低。

因此,在编写代码之前,请在代码中添加类似的内容:

char buf[256 * 1024];
mystream.rdbuf()->pubsetbuf(buf, sizeof(buf));

注意:您可以找到完整的示例应用程序here