在c ++中快速读取文本文件
我目前正在用c ++编写一个程序,其中包括阅读大量的大文本文件。 每行至less有400,000行,极端情况下每行有4000或更多字符。 为了testing,我使用ifstream和cplusplus.com提供的实现来阅读其中的一个文件。 花了大约60秒,这太长了。 现在我想知道,是否有一个简单的方法来提高阅读速度?
编辑:我使用的代码是或多或less这个:
string tmpString; ifstream txtFile(path); if(txtFile.is_open()) { while(txtFile.good()) { m_numLines++; getline(txtFile, tmpString); } txtFile.close(); }
编辑2:我读的文件只有82 MB大。 我主要说可能达到4000,因为我觉得可能有必要知道为了做缓冲。
编辑3:谢谢大家的回答,但看起来好像没有太大的空间来改善我的问题。 我不得不使用readline,因为我想要计算行数。 将ifstream实例化为二进制不会使读取速度更快。 我会尽可能地并行化,至less应该起作用。
编辑4:所以显然有一些事情我可以。 非常感谢你把这么多的时间放在这里,我很感激! =)
更新:请确保在初始答案下面检查(令人惊讶的)更新
内存映射文件给我很好的1 :
#include <boost/iostreams/device/mapped_file.hpp> // for mmap #include <algorithm> // for std::find #include <iostream> // for std::cout #include <cstring> int main() { boost::iostreams::mapped_file mmap("input.txt", boost::iostreams::mapped_file::readonly); auto f = mmap.const_data(); auto l = f + mmap.size(); uintmax_t m_numLines = 0; while (f && f!=l) if ((f = static_cast<const char*>(memchr(f, '\n', lf)))) m_numLines++, f++; std::cout << "m_numLines = " << m_numLines << "\n"; }
这应该是相当快的。
更新
如果它可以帮助你testing这种方法,这里是一个使用mmap
的版本,而不是使用Boost: 在Coliru上看到它
#include <algorithm> #include <iostream> #include <cstring> // for mmap: #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> const char* map_file(const char* fname, size_t& length); int main() { size_t length; auto f = map_file("test.cpp", length); auto l = f + length; uintmax_t m_numLines = 0; while (f && f!=l) if ((f = static_cast<const char*>(memchr(f, '\n', lf)))) m_numLines++, f++; std::cout << "m_numLines = " << m_numLines << "\n"; } void handle_error(const char* msg) { perror(msg); exit(255); } const char* map_file(const char* fname, size_t& length) { int fd = open(fname, O_RDONLY); if (fd == -1) handle_error("open"); // obtain file size struct stat sb; if (fstat(fd, &sb) == -1) handle_error("fstat"); length = sb.st_size; const char* addr = static_cast<const char*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0u)); if (addr == MAP_FAILED) handle_error("mmap"); // TODO close fd at some point in time, call munmap(...) return addr; }
更新
我可以通过查看GNU coreutils wc
的源代码find最后一点的性能。 令我惊讶的是,使用以下(大大简化)的代码从wc
运行的内存映射文件占用了大约84%的时间 :
static uintmax_t wc(char const *fname) { static const auto BUFFER_SIZE = 16*1024; int fd = open(fname, O_RDONLY); if(fd == -1) handle_error("open"); /* Advise the kernel of our access pattern. */ posix_fadvise(fd, 0, 0, 1); // FDADVICE_SEQUENTIAL char buf[BUFFER_SIZE + 1]; uintmax_t lines = 0; while(size_t bytes_read = read(fd, buf, BUFFER_SIZE)) { if(bytes_read == (size_t)-1) handle_error("read failed"); if (!bytes_read) break; for(char *p = buf; (p = (char*) memchr(p, '\n', (buf + bytes_read) - p)); ++p) ++lines; } return lines; }
1例如在这里看到的基准: 如何快速parsingC ++空间分隔的花车?
4000 * 400,000 = 1.6 GB如果你是硬盘驱动器不是一个SSD,你可能会获得〜100 MB / s的顺序读取。 I / O只有16秒。
既然你没有详细说明你使用的特定代码,或者你需要如何parsing这些文件(你是否需要逐行读取,系统是否有大量的RAM,可以将整个文件读入一个大的RAM缓冲区然后parsing它?)你可以做什么来加快这个过程。
内存映射文件在顺序读取文件时不会提供任何性能改进。 也许手动parsing大块为新行,而不是使用“getline”会提供一个改进。
编辑后做一些学习(谢谢@ sehe)。 这是我可能使用的内存映射解决scheme。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <errno.h> int main() { char* fName = "big.txt"; // struct stat sb; long cntr = 0; int fd, lineLen; char *data; char *line; // map the file fd = open(fName, O_RDONLY); fstat(fd, &sb); //// int pageSize; //// pageSize = getpagesize(); //// data = mmap((caddr_t)0, pageSize, PROT_READ, MAP_PRIVATE, fd, pageSize); data = mmap((caddr_t)0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); line = data; // get lines while(cntr < sb.st_size) { lineLen = 0; line = data; // find the next line while(*data != '\n' && cntr < sb.st_size) { data++; cntr++; lineLen++; } /***** PROCESS LINE *****/ // ... processLine(line, lineLen); } return 0; }
你是否需要同时阅读所有文件? (例如在你的应用程序的开始)
如果这样做,考虑并行操作。
无论哪种方式,考虑使用二进制stream,或无缓冲读取的数据块。
使用Random file access
或使用binary mode
。 对于顺序,这是很大的,但仍然取决于你在读什么。
作为一个在竞争性编程方面有一点背景的人,我可以告诉你:至less对于整数parsing这样的简单事情,C中的主要成本是locking文件stream(默认为multithreading)。 使用unlocked_stdio
版本( fgetc_unlocked()
, fread_unlocked()
)。 对于C ++,常见的知识是使用std::ios::sync_with_stdio(false)
但我不知道它是否像unlocked_stdio
一样快。
这里的参考是我的标准整数parsing代码。 它比scanf快很多,正如我之前所说的,主要是因为没有lockingstream。 对于我来说,它和我以前使用的最好的手工编码的mmap或定制缓冲版本一样快,没有疯狂的维护债务。
int readint(void) { int n, c; n = getchar_unlocked() - '0'; while ((c = getchar_unlocked()) > ' ') n = 10*n + c-'0'; return n; }
(注意:只有在两个整数之间只有一个非数字字符的情况下才能使用)。
当然,如果可能的话避免内存分配…