在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());
读取一个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>());
代码很好,但是非常慢。读取同样大小的1.6 MB文件大约需要2/3秒钟。我知道这可能不是最好的读取文件的方式,但为什么会这么慢呢?
传统的文件读取方式是这样的(我只谈论read函数):
- istream包含一个filebuf,它包含来自文件的数据块。 - read函数从filebuf调用sgetn,将字符一个一个地复制(没有memcpy),从内部缓冲区复制到"data"的缓冲区。 - 当filebuf中的数据完全读取时,filebuf会从文件中读取下一个块。
当使用istream_iterator读取文件时,过程如下:
- 向量调用*iterator以获取下一个字符(这只是读取一个变量),将其添加到末尾并增加自身大小。 - 如果向量分配的空间已满(这不太常见),则执行重定位。 - 然后调用++iterator从流中读取下一个字符(使用char参数的operator >>,这肯定只是调用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());