使用 ofstream 进行缓冲文本输出以获得性能
Using ofstream for buffered text output to gain performance
我需要编写一个程序,该程序将在输出文件中写入许多字符。我的程序还需要编写换行符以获得更好的格式。我知道ofstream
是一个缓冲流,如果我们对文件 io 使用缓冲流,我们会获得性能。但是,如果我们使用std::endl
输出将被刷新,并且由于缓冲输出,我们将失去任何潜在的性能增益。
我想如果我将 'n'
用于新行,则只有在我们std::endl
时才会刷新输出。这是对的吗?是否有任何技巧可用于在文件输出期间获得性能提升?
注意:我想在文件写入操作完成时刷新缓冲输出。我认为通过这种方式,我可以最大限度地减少文件 I/O,从而获得性能。
通常,如果需要最大性能,流类的用户不应弄乱流的刷新:流在缓冲区已满时在内部刷新其缓冲区。这实际上比等到所有输出准备就绪更有效,尤其是对于大文件:缓冲数据在可能仍在内存中时写入。如果您创建一个巨大的缓冲区并且只写入一次,则虚拟内存系统会将部分数据放入光盘而不是文件。它需要从光盘中读取并再次写入。
关于std::endl
的要点是,人们滥用它作为行尾,导致缓冲区刷新,并且他们不知道性能影响。std::endl
的目的是让人们控制在合理的点刷新文件。为了有效,他们需要知道自己在做什么。可悲的是,有太多人不知道std::endl
做什么,他们宣传它的使用作为行尾,以至于它被用于许多明显错误的地方。
也就是说,以下是您可能想要尝试提高性能的一些内容。我假设您需要格式化输出(使用 std::ofstream::write()
不会给您(。
- 显然,除非必须,否则不要使用
std::endl
。如果编写代码已存在并在许多位置使用std::endl
(其中一些位置可能超出您的控制范围(,则可以使用筛选流缓冲区,该缓冲区使用其大小合理的内部缓冲区,并且不会将对其sync()
函数的调用转发到基础流缓冲区。虽然这涉及额外的副本,但这比一些虚假刷新要好,因为这些刷新要贵几个数量级。 - 尽管它不应该对
std::ofstream
产生影响,但调用std::ios_base::sync_with_stdio(false)
用于影响某些实现的性能。如果这有影响,您需要考虑使用不同的 IOstream 实现,因为在性能方面可能存在更多问题。 - 确保您使用的是
std::locale
,其std::codecvt<...>
在调用其always_noconv()
时返回true
。这可以通过使用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
,将流格式化用于内存映射文件(或者更一般地说,与任何类型的输出接口一起使用(。我发现内存映射有时在将它们与std::istream
一起使用时有效。在许多情况下,差异并不重要。
很久以前,我编写了自己的 IOStreams 和语言环境实现,它没有遇到上面提到的一些性能问题(它可以从我的网站上获得,但它有点陈旧,我已经有近 10 年没有碰过它了(。在这个实现中还有很多东西可以改进,但我还没有准备好在某处发布的最新实现。很快,希望 - 这是我近 10 年来一直在思考的事情,不过......
n
不会(不一定(刷新输出,而打印std::endl
或std::flush
会。
如果您想要快速写入并且不在乎数据是否在那里,直到您完全完成,那么请使用n
进行所有写入,并且不必担心(因为关闭文件也会刷新流(。
如果你仍然没有得到你想要的性能,你可以使用 fstream::read(char*, int( - 它允许你读取你想要的任何大小的数据块(尝试更大的块,看看是否有帮助(。
,endl
刷新流。不要将其用于大文件。
此外,请确保设置流缓冲区。至少在未设置缓冲区时,MSVC 实现一次将 1 个字符复制到filebuf
(请参阅streambuf::xsputn
(。这可能会使应用程序受 CPU 限制,从而导致较低的 I/O 速率。
因此,在编写之前,请在代码中添加类似以下内容:
char buf[256 * 1024];
mystream.rdbuf()->pubsetbuf(buf, sizeof(buf));
注意:您可以在此处找到完整的示例应用程序。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 递归函数计算序列中的平方和(并输出过程)
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 请解释"函数1(p1,p2,p3);"的输出
- C++:将控制台输出存储在宏中更好吗
- OpenMP阵列性能较差
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 递归列出所有目录中的C++与Python与Ruby的性能
- 为什么我的代码在输出中增加了93天
- 如何从void函数输出字符串
- 输入到文件并输出到另一个文件,并将流文件传递给函数
- AES加密到解密未正确输出
- 如何将c++程序的一些输出传递给shell,以便在shell中使用
- 使用C++程序合并排序没有得到正确的输出
- 性能输出中有奇怪的字符.
- 从标准输出读取奇怪的性能问题
- 使用 ofstream 进行缓冲文本输出以获得性能
- Windows和OSX之间iostream控制台输出的性能差异
- c++ stringstream输出性能