用C++读取文件的奇特方法:奇怪的性能问题

Tom*_*a17 9 c++ performance iterator file

在C++中读取文件的常用方法是:

std::ifstream file("file.txt", std::ios::binary | std::ios::ate);
std::vector<char> data(file.tellg());
file.seekg(0, std::ios::beg);
file.read(data.data(), data.size());
Run Code Online (Sandbox Code Playgroud)

读取1.6 MB文件几乎是即时的.

但是最近,我发现了std :: istream_iterator并且想要尝试它以编码一个漂亮的单行方式来读取文件的内容.像这样:

std::vector<char> data(std::istream_iterator<char>(std::ifstream("file.txt", std::ios::binary)), std::istream_iterator<char>());
Run Code Online (Sandbox Code Playgroud)

代码很好,但慢.读取相同的1.6 MB文件大约需要2/3秒.我知道它可能不是读取文件的最佳方式,但为什么它这么慢?

以经典方式读取文件是这样的(我只谈论读取函数):

  • istream包含一个filebuf,其中包含文件中的数据块
  • read函数从filebuf 调用sgetn,它将chars从内部缓冲区逐个复制(无memcpy)到"data"的缓冲区
  • 当完全读取filebuf中的数据时,filebuf从文件中读取下一个块

当您使用istream_iterator读取文件时,它如下所示:

  • 向量调用*iterator来获取下一个char(这只是读取一个变量),将它添加到结尾并增加它自己的大小
  • 如果向量的已分配空间已满(不常发生),则执行重定位
  • 然后它调用++ iterator从流中读取下一个char(operator >> with char参数,当然只调用filebuf的sbumpc函数)
  • 最后它将迭代器与结束迭代器进行比较,这是通过比较两个指针来完成的

我必须承认,第二种方式效率不高,但它比第一种方式慢至少200倍,这怎么可能?

我认为性能杀手是重定位或插入,但我尝试创建一个完整的向量并调用std :: copy,它同样慢.

// also very slow:
std::vector<char> data2(1730608);
std::copy(std::istream_iterator<char>(std::ifstream("file.txt", std::ios::binary)), std::istream_iterator<char>(), data2.begin());
Run Code Online (Sandbox Code Playgroud)

Tho*_*tit 7

你应该比较苹果到苹果.

您的第一个代码读取未格式化的二进制数据,因为您使用函数成员"read".并不是因为你顺便使用std :: ios_binary,请参阅http://stdcxx.apache.org/doc/stdlibug/30-4.html以获得更多解释,但简而言之:"二进制打开模式的效果是经常被误解.它不会将插入器和提取器置于二进制模式,因此会抑制它们通常执行的格式化.二进制输入和输出完全由basic_istream <> :: read()和basic_ostream <> :: write()完成"

因此,使用istream_iterator的第二个代码读取格式化文本.它的速度慢了.

如果要读取未格式化的二进制数据,请使用istreambuf_iterator:

#include <fstream>
#include <vector>
#include <iterator>

std::ifstream file( "file.txt", std::ios::binary);
std::vector<char> buffer((std::istreambuf_iterator<char>(file)),
                          std::istreambuf_iterator<char>());   
Run Code Online (Sandbox Code Playgroud)

在我的平台(VS2008)上,istream_iterator比read()慢约x100.istreambuf_iterator表现更好,但仍然比read()慢x10.