C 和 C++ 样式文件 IO 之间的性能差异

Posted

技术标签:

【中文标题】C 和 C++ 样式文件 IO 之间的性能差异【英文标题】:Performance Difference Between C and C++ Style File IO 【发布时间】:2013-07-02 07:54:17 【问题描述】:

我一直听说 C++ 文件 I/O 操作比 C 风格的 I/O 慢得多。但是我没有找到任何关于它们实际上有多慢的实用参考,所以我决定在我的机器上测试它(Ubuntu 12.04,GCC 4.6.3,ext4 分区格式)。

首先我在磁盘中写入了一个~900MB 的文件。

C++ (ofstream):163s

ofstream file("test.txt");
    
for(register int i = 0; i < 100000000; i++) 
    file << i << endl;

C (fprintf):12 秒

FILE *fp = fopen("test.txt", "w");
    
for(register int i = 0; i < 100000000; i++) 
    fprintf(fp, "%d\n", i);

我期待这样的输出,它表明在 C++ 中写入文件比在 C 中慢得多。然后我使用 C 和 C++ I/O 读取相同的文件。是什么让我惊呼从文件读取时性能几乎没有差异。

C++ (ifstream):12 秒

int n;
ifstream file("test.txt");

for(register int i = 0; i < 100000000; i++) 
    file >> n;

C (fscanf):12 秒

FILE *fp = fopen("test.txt", "r");
    
for(register int i = 0; i < 100000000; i++) 
    fscanf(fp, "%d", &n);

那么,为什么使用流执行写入需要这么长时间?或者,为什么使用流读取比写入快?

结论:罪魁祸首是std::endl,正如答案和 cmets 所指出的那样。换行 file &lt;&lt; i &lt;&lt; endl;file &lt;&lt; i &lt;&lt; '\n'; 已将运行时间从 163 秒减少到 16 秒。

【问题讨论】:

你关闭优化了吗?也许在 ifstream 情况下编译器会看到您一直覆盖 n 并且只是移动文件指针而不实际从文件中读取? 叹息。 没有区别,你的基准有缺陷。如果有的话,C++ 格式的输入/输出更快。不是有无数个这样的副本吗? 我不否认 fstreamcstdio 慢,但这些差异似乎比我从测量结果中预期的要大一些。不是您以低(或没有)优化级别运行的情况吗?由于流主要是通过模板实现的,因此它会被编译到您的代码中,其中cstdio 类型的函数被编译到库中并具有更高级别的优化。 你在作弊。 std::endl 比您在 fprintf 中输出的 \n 多。它导致流冲洗。所以试试file &lt;&lt; i &lt;&lt; '\n' Why C++ output is too much slower than C? 的可能重复项 【参考方案1】:

您正在使用endl 打印换行符。这就是这里的问题,因为它更多不仅仅是打印换行符 - endl刷新 缓冲区这是一项昂贵的操作(如果你在每次迭代中都这样做)。

使用\n,如果你是这样想的话:

file << i << '\n';

而且,必须在发布模式下编译您的代码(即打开优化)。

【讨论】:

谢谢,这就是问题所在。更改为 '\n' 将时间缩短到 16 秒! 那么实际上没有“C 和 C++ 样式文件 IO 的性能差异”吗? @AlexandruBarbarosie:是的,没有理由这样做。 嗯,还是 16s 对 12s,25% 的差异也不算微不足道吧? 没有问题,here you go。在 MSVC2015U3、Windows 10 x64 上试过,性能差异 30%。但! FreeBSD 11 (clang 3.4.1) 上的相同代码在两种情况下显示完全相同的结果。所以我猜它是依赖于实现的。【参考方案2】:

不,C++ 输入/输出并不比 C 慢很多 - 如果有的话,现代实现在格式化输入/输出上应该稍微快一些,因为它不需要解析格式字符串,而是在通过流操作符的链接编译时间。

以下是在基准测试中需要考虑的一些注意事项:

使用全面优化 (-O3) 进行编译以获得公平的比较。 适当的基准测试需要估计偏差——在实践中,这意味着您需要重复测试并将它们交错。目前,您的代码对来自后台进程的干扰并不健壮。您还应该报告重复运行的汇总统计数据,以捕捉扭曲估计值的异常值。 禁用 C++ 流与 C 流的同步 (std::ios_base::sync_with_stdio(false);) 使用'\n' 而不是(刷新)std::endl 不要使用register 声明——它根本没有区别,现代编译器可能无论如何都会忽略它。

【讨论】:

【参考方案3】:

使用fstream 处理大文件时,请确保设置流缓冲区>0

与直觉相反,禁用流缓冲会显着降低性能。至少 MSVC 2015 实现在未设置缓冲区时将 1 个字符复制到 filebuf(请参阅 streambuf::xsputn),这会使您的应用程序受 CPU 限制,从而导致较低的I/O 速率。

const size_t bufsize = 256*1024;
char buf[bufsize];
mystream.rdbuf()->pubsetbuf(buf, bufsize);

您可以找到完整的示例应用程序here。

【讨论】:

以上是关于C 和 C++ 样式文件 IO 之间的性能差异的主要内容,如果未能解决你的问题,请参考以下文章

SVG 属性和样式之间是不是存在性能差异?

Linux 与 Windows,C++ 运行时性能差异超过读取 CSV 文件

在 C++ 中找不到 AoS 和 SoA 之间的性能差异

RcppArmadillo 和 Armadillo 之间的性能差异

可见性之间的性能差异:隐藏和显示:无

C ++中的指针和引用之间是不是存在任何开销差异[重复]