【问题标题】:read huge text file line by line in C++ with buffering使用缓冲在 C++ 中逐行读取巨大的文本文件
【发布时间】:2014-09-11 03:26:32
【问题描述】:

我需要在 C++ 中逐行从磁盘读取巨大的 35G 文件。目前我这样做的方式如下:

ifstream infile("myfile.txt");
string line;
while (true) {
    if (!getline(infile, line)) break;
    long linepos = infile.tellg();
    process(line,linepos);
}

但它给了我大约 2MB/秒的性能,尽管文件管理器以 100Mb/s 的速度复制文件。我猜getline() 没有正确缓冲。请提出某种缓冲的逐行阅读方法。

UPD:process() 不是瓶颈,没有 process() 的代码运行速度相同。

【问题讨论】:

  • getline 不做任何缓冲,istream 做。为什么一定要逐行阅读?为什么不一次读几百万行呢?
  • 是什么让你相信瓶颈是getline,而不是process
  • 您编译时是否启用了优化?对于 g++ 和 clang,使用 -O2 或 -O3,对于 Visual C++,使用 Release 构建。
  • @IgorTandetnik,查看我的更新。
  • 尝试不使用tellg。使用纯 getline 应该会更快。

标签: c++ performance stl buffering


【解决方案1】:

使用标准 IO 流,您将无法获得接近线速度的任何地方。缓冲与否,几乎任何解析都会将你的速度降低几个数量级。我对由两个整数和每行双精度组成的数据文件进行了实验(常春藤桥芯片,SSD):

  • 各种组合的 IO 流:~10 MB/s。纯解析 (f >> i1 >> i2 >> d) 比将 getline 解析为字符串后跟 sstringstream 解析要快。
  • fscanf 等 C 文件操作的速度约为 40 MB/s。
  • getline 没有解析:180 MB/s。
  • fread:500-800 MB/s(取决于文件是否被操作系统缓存)。

I/O 不是瓶颈,解析才是。换句话说,您的process 可能是您的慢点。

所以我写了一个并行解析器。它由任务组成(使用 TBB 管道):

  1. fread 大块(一次一个这样的任务)
  2. 重新排列块,这样一行就不会在块之间分割(一次一个这样的任务)
  3. 解析块(许多这样的任务)

我可以有无限的解析任务,因为我的数据无论如何都是无序的。如果你的不是,那么这对你来说可能不值得。 这种方法可以让我在 4 核 IvyBridge 芯片上获得大约 100 MB/s 的速度。

【讨论】:

  • ifstream.read 应该具有与fread 相同的性能(它也不进行解析);使用它可能需要对现有代码进行较少的更改
  • 该文件是否为您的getline 测试缓存?我看到询问者的代码为 680-800MB/秒(process() 为空),而没有说明为 1GB/秒。 (gcc-4.6.3, -O0)
  • @user2313838 是的,它已被缓存。我的代码看起来也很像提问者的。
  • MacOS/SSD/CoreI7(2xPhysical):ifstream iff("out.mp"); while(iff.good()) { getline(iff, line); // get line from file } 16727 毫秒,文件大小:371M
  • @ArthurKushman 这只有 22 MB/s,你应该能得到更多。
【解决方案2】:

我已经从我的 java 项目中翻译了我自己的缓冲代码,它可以满足我的需要。我必须使用定义来克服 M$VC 2010 编译器tellg 的问题,这总是在大文件上给出错误的负值。该算法提供了约 100MB/s 的所需速度,尽管它做了一些无用的新 []。

void readFileFast(ifstream &file, void(*lineHandler)(char*str, int length, __int64 absPos)){
        int BUF_SIZE = 40000;
        file.seekg(0,ios::end);
        ifstream::pos_type p = file.tellg();
#ifdef WIN32
        __int64 fileSize = *(__int64*)(((char*)&p) +8);
#else
        __int64 fileSize = p;
#endif
        file.seekg(0,ios::beg);
        BUF_SIZE = min(BUF_SIZE, fileSize);
        char* buf = new char[BUF_SIZE];
        int bufLength = BUF_SIZE;
        file.read(buf, bufLength);

        int strEnd = -1;
        int strStart;
        __int64 bufPosInFile = 0;
        while (bufLength > 0) {
            int i = strEnd + 1;
            strStart = strEnd;
            strEnd = -1;
            for (; i < bufLength && i + bufPosInFile < fileSize; i++) {
                if (buf[i] == '\n') {
                    strEnd = i;
                    break;
                }
            }

            if (strEnd == -1) { // scroll buffer
                if (strStart == -1) {
                    lineHandler(buf + strStart + 1, bufLength, bufPosInFile + strStart + 1);
                    bufPosInFile += bufLength;
                    bufLength = min(bufLength, fileSize - bufPosInFile);
                    delete[]buf;
                    buf = new char[bufLength];
                    file.read(buf, bufLength);
                } else {
                    int movedLength = bufLength - strStart - 1;
                    memmove(buf,buf+strStart+1,movedLength);
                    bufPosInFile += strStart + 1;
                    int readSize = min(bufLength - movedLength, fileSize - bufPosInFile - movedLength);

                    if (readSize != 0)
                        file.read(buf + movedLength, readSize);
                    if (movedLength + readSize < bufLength) {
                        char *tmpbuf = new char[movedLength + readSize];
                        memmove(tmpbuf,buf,movedLength+readSize);
                        delete[]buf;
                        buf = tmpbuf;
                        bufLength = movedLength + readSize;
                    }
                    strEnd = -1;
                }
            } else {
                lineHandler(buf+ strStart + 1, strEnd - strStart, bufPosInFile + strStart + 1);
            }
        }
        lineHandler(0, 0, 0);//eof
}

void lineHandler(char*buf, int l, __int64 pos){
    if(buf==0) return;
    string s = string(buf, l);
    printf(s.c_str());
}

void loadFile(){
    ifstream infile("file");
    readFileFast(infile,lineHandler);
}

【讨论】:

    【解决方案3】:

    使用行解析器或编写相同的。这是 sourceforge http://tclap.sourceforge.net/ 中的一个示例,如有必要,请放入缓冲区。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-11
      • 1970-01-01
      • 2013-04-07
      相关资源
      最近更新 更多