c++ - 用 C++ 快速读取文本文件

标签 c++ performance io ifstream

我目前正在用 C++ 编写一个程序,其中包括读取大量大型文本文件。每个都有约 400.000 行,在极端情况下,每行有 4000 个或更多字符。只是为了测试,我使用 ifstream 和 cplusplus.com 提供的实现读取了其中一个文件。花了大约60秒,这太长了。现在我想知道,有没有一种直接的方法可以提高阅读速度?

编辑: 我使用的代码或多或少是这样的:

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 实例化为二进制文件也不会使读取速度更快。我会尽可能多地并行化它,至少应该可以。

编辑 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', l-f))))
            m_numLines++, f++;

    std::cout << "m_numLines = " << m_numLines << "\n";
}

这应该很快。

更新

如果它可以帮助您测试这种方法,这里有一个版本 using mmap 直接代替使用 Boost:see it live on 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', l-f))))
            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 的源代码,我发现了我可以从中挤出的最后一点性能。令我惊讶的是,使用改编自 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 参见例如这里的基准:How to parse space-separated floats in C++ quickly?

关于c++ - 用 C++ 快速读取文本文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17925051/

相关文章:

c++ - 使用父窗口的控件打开新窗口

c++最快的循环扫描3D vector

c - 通过 Return 与传递指针获取数据

java - 执行字符串分割时出现 NullPointer 异常

java - 请帮我理解这行代码

c++ - 在 Qt5 的 QWidget 中添加新按钮

c++ - 使用 enable_if 的模板特化

list - Haskell获取IO列表元素

c++ - OpenCL 缓冲区分配和映射最佳实践

java - jmeter,模拟线程数较少的用户