【问题标题】:How to read 4GB file on 32bit system如何在 32 位系统上读取 4GB 文件
【发布时间】:2014-09-28 17:17:30
【问题描述】:

在我的情况下,我有不同的文件,假设我有 >4GB 的数据文件。我想逐行读取该文件并处理每一行。我的限制之一是软件必须在 32 位 MS Windows 或 64 位上运行,并具有少量 RAM(最小 4GB)。您还可以假设这些行的处理不是瓶颈。

在当前解决方案中,我通过ifstream 读取该文件并复制到某个字符串。这是 sn-p 的样子。

std::ifstream file(filename_xml.c_str());
uintmax_t m_numLines = 0;
std::string str;
while (std::getline(file, str))
{
    m_numLines++;
}

好吧,这行得通,但慢慢来,我的 3.6 GB 数据是时候了:

real    1m4.155s
user    0m0.000s
sys     0m0.030s

我正在寻找一种比这更快的方法,例如我发现 How to parse space-separated floats in C++ quickly? 并且我喜欢使用 boost::mapped_file 提供的解决方案,但我遇到了另一个问题,如果我的文件很大并且在我的案例文件 1GB 大足以放弃整个过程。我必须关心内存中的当前数据,可能使用该工具的人安装的 RAM 不超过 4 GB。

所以我从 boost 中找到了 mapped_file,但在我的情况下如何使用它?是否可以部分读取该文件并接收这些行?

也许你有另一个更好的解决方案。我只需要处理每一行。

谢谢,
巴特

【问题讨论】:

  • 您只能映射文件的一部分。
  • memmapping 会将整个文件映射到内存空间。这是不可能的,因为您的文件会占用进程的整个可寻址空间。您需要“窗口化”文件,因此在任何给定时间通过您的 memmap 区域只能看到文件的较小部分。
  • 如果连续运行两次,统计数据是多少?用户或系统时间几乎为零的事实意味着大部分时间都花在了 I/O 上。除非您有足够的内存来缓存整个文件,否则转到内存映射文件不会提高速度(因为需要分页数据)。
  • 典型的 7200 rpm 主轴磁盘驱动器最多可以以 60 MB/秒的速度读取。 3.6 GB 需要 1 分钟才能读取,无论您编写哪种代码。您需要更快的磁盘或停止等待程序完成。
  • @bioky - 当不使用磁盘但数据来自文件系统缓存并且机器有足够的 RAM 时,它会快很多。重复运行程序时的典型基准风险。

标签: c++ boost large-files 32-bit data-processing


【解决方案1】:

很高兴看到你在How to parse space-separated floats in C++ quickly?找到我的基准

您似乎真的在寻找计算行数的最快方法(或任何线性单遍分析),我在这里做了类似的分析和基准测试

有趣的是,您会发现性能最高的代码根本不需要依赖内存映射。

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;
}

【讨论】:

    【解决方案2】:

    具有小内存的 64 位系统的情况应该可以加载一个大文件 - 这完全与地址空间有关 - 尽管在这种情况下它可能比“最快”选项慢,但它真的取决于内存中还有什么以及有多少内存可用于将文件映射到。在 32 位系统中,它不起作用,因为指向文件映射的指针最多不会超过大约 3.5GB - 通常大约 2GB 是最大值 - 再次取决于可用的内存地址要将文件映射到的操作系统。

    但是,内存映射文件的好处非常小 - 大部分时间都花在了实际读取数据上。使用内存映射的节省来自于一旦将数据加载到 RAM 就不必复制数据。 (当使用其他文件读取机制时,read 函数会将数据复制到提供的缓冲区中,其中内存映射文件会将其直接填充到正确的位置)。

    【讨论】:

    • 我会尝试在 x64 上编译映射,也许系统会通过分页完成剩下的工作。但另一方面,我认为直接归档窗口的方式还是不错的。在这个想法中,我可以更改一些处理内容以省略该文件中的大量字节,这可能会更快一些。你怎么看?
    【解决方案3】:

    您可能想考虑增加 ifstream 的缓冲区 - 默认缓冲区通常很小,这会导致大量昂贵的读取。

    你应该可以使用类似的东西来做到这一点:

    std::ifstream file(filename_xml.c_str());
    char buffer[1024*1024];
    file.rdbuf()->pubsetbuf(buffer, 1024*1024);
    
    uintmax_t m_numLines = 0;
    std::string str;
    while (std::getline(file, str))
    {
        m_numLines++;
    }
    

    查看这个问题了解更多信息:

    How to get IOStream to perform better?

    【讨论】:

      【解决方案4】:

      由于这是windows,你可以使用带有“ex”后缀的原生windows文件函数:

      windows file management functions

      特别是 GetFileSizeEx(), SetFilePointerEx(), ... 等函数。读写函数限制为 32 位字节数,读写“ex”函数用于异步 I/O,而不是处理大文件。

      【讨论】:

        猜你喜欢
        • 2023-03-21
        • 2013-03-25
        • 2010-09-13
        • 1970-01-01
        • 1970-01-01
        • 2011-09-08
        • 2011-08-20
        • 1970-01-01
        • 2014-11-12
        相关资源
        最近更新 更多