【问题标题】:c++ read small portions of big number of filesc ++读取大量文件的一小部分
【发布时间】:2021-01-06 23:01:52
【问题描述】:

我有一个相对简单的问题要问,许多编程语言一直在讨论哪种方法可以提供最快的文件读取。主要讨论read()mmap()。作为一个也参与了这些辩论的人,我没有找到我当前问题的答案,因为大多数答案都有助于读取文件很大的情况(例如,如何读取 10 TB 的文本文件......) .

但我的问题有点不同,我有很多文件,比如说 1 亿个。我想从这些文件中读取前 1-2 行。文件是 10 kb 还是 100 TB 无关紧要。我只想要每个文件的前一两行。所以我想避免读取或缓冲文件中不必要的部分。我的知识不足以彻底测试哪种方法更快,或者首先发现我的所有选择。

我在做什么正确的知道:(我目前正在做这个多线程)

for(const auto& p: std::filesystem::recursive_directory_iterator(path)) {
    if (!std::filesystem::is_directory(p)) {
        std::ifstream   read_file(p.path().string());
        if (read_file.is_open()) {
            while (getline(read_file, line)) {
                    // Get two lines here.
            }
        }
    }
}

在这种情况下,C++ 或 linux 环境为我提供了什么?是否有更快或更有效的方法来读取数百万个文件的一小部分?

感谢您的宝贵时间。

信息:我可以访问C++20 和 Ubuntu 18.04

【问题讨论】:

  • 你可以去掉 if (!std::filesystem::is_directory(p)) { 并让 ifstream 构造函数失败,如果它是一个目录。这节省了fstat 调用
  • @Jean-FrançoisFabre: ifstream 将愉快地“打开”一个目录....
  • 不在我的机器上,它不在
  • ifstream 在开始时引入了过多的开销。就性能而言,您将无法超越智能使用 mmap
  • 解决这个问题的正确方法是在写入文件时对其进行索引。任何语言都无法“快速”访问数百万个文件。

标签: c++ c linux performance file


【解决方案1】:

你可以保存一个对fstat的底层调用测试路径是否是目录,然后依赖is_open测试

#include <iostream>
#include <fstream>
#include <filesystem>
#include <string>

int main()
{
 std::string line,path=".";
 for(const auto& p: std::filesystem::recursive_directory_iterator(path)) {
 { 
        std::ifstream   read_file(p.path().string());
        if (read_file.is_open()) {
        std::cout << "opened: " << p.path().string() << '\n';
           while (getline(read_file, line)) {
                    // Get two lines here.
            }
        }
    }
}
}

至少在 Windows 上,此代码会跳过目录。正如 cmets 中所建议的那样,is_open 甚至可以跳过测试,因为 getline 也不会从目录中读取任何内容。

不是最干净的,但如果它可以节省时间,那就值得了。

【讨论】:

    【解决方案2】:

    在 Linux 下访问文件的程序中的任何函数都会导致调用一些“系统调用”(例如read())。

    某些编程语言中的所有其他可用函数(如fread()fgets()std::filesystem ...)调用函数或方法,这些函数或方法又调用一些系统调用。

    因此,您不能比直接调用系统调用更快。

    我不能 100% 确定,但我认为在大多数情况下,open()read()close() 的组合将是从文件开头读取数据的最快方法。

    (如果数据不在文件开头,pread() 可能比read() 快;我不确定。)

    请注意,read() 不会读取特定数量的行,而是读取特定数量的字节(例如,读取到 char 的数组),因此您必须找到行的结尾“手动”通过在 char 数组中搜索 '\n' 字符和/或文件末尾。

    不幸的是,一行可能比您预期的要长得多,因此从文件中读取前 N 个字节不包含前 M 行,您必须调用再次read()

    在这种情况下,这取决于您的系统(例如文件系统甚至硬盘)在每次调用 read() 时应该读取多少字节才能获得最佳性能。

    示例:假设在所有文件的 75% 中,前 N 行位于文件的前 512 个字节中;在其他 25% 的文件中,前 N 行的总和超过 512 个字节。

    在某些计算机上,一次读取 1024 字节可能需要与读取 512 字节几乎相同的时间,但两次读取 512 字节将比一次读取 1024 字节慢得多;在这样的计算机上,一次read() 1024 字节是有意义的:您为 25% 的文件节省了大量时间,而为其他 75% 的文件浪费的时间很少。

    在其他计算机上,读取 512 字节明显快于读取 1024 字节;在这样的计算机上,read() 512 字节会更好:读取 1024 字节只会在处理 25% 的文件时为您节省很少的时间,但在处理其他 75% 的文件时会花费您很多时间。

    我认为在大多数情况下,这个“最佳值”将是 512 字节的倍数,因为大多数现代文件系统以 512 字节的倍数为单位组织文件。

    【讨论】:

    • 目前我在做getline(read_file, line)。但我可以说我想要的数据在文件的开头,在第一行,幸运的是第一行确实很短(只包含我需要的数据)。这就是为什么我想制定一种策略来更好地处理我的情况,而不是简单的文件读取。
    • @Rockybilly 如果您确定始终在文件的第一个 N 字节和 N&lt;=512 中找到信息,则序列 open()read(...,N)close() 似乎成为你拥有的最快的可能性。如果速度很重要,您还应该考虑不要使用数据类型string,而是使用char 的数组。处理每个文件时应避免使用动态内存分配(newmalloc()...),而是在开始时只分配一次您需要的所有资源。
    【解决方案3】:

    我只是在输入类似于 Martin Rosenau 答案的内容(当他弹出时):非结构化读取最大长度为两行。但我会更进一步:将该文本缓冲区与相应的文件名排队,然后让另一个线程解析/分析它。如果解析和读取的时间差不多,可以节省一半的时间。如果需要更长的时间(不太可能) - 您可以使用多个线程并节省更多。

    旁注 - 你不应该并行阅读(尝试过)。

    这可能值得尝试:您可以打开一个文件,在继续打开下一个文件的同时异步读取它吗?我不知道是否有任何操作系统可以重叠这些东西。

    【讨论】:

    • 我能够并行化这个,因为文件分布在一些文件夹中(消除了首先知道文件以在线程之间共享它们的需要),我能够将 disk_read 增加到 6 倍。但是我打开这个问题的原因可能是我从文件中读取的内容超出了必要的范围,因此我的 ssd 可能会在不必要的操作上花费一些资源。想知道是否存在类似神奇的低级系统调用(或者当然是更合乎逻辑的东西):)
    • 你是如何“尝试”的?今天的许多系统都针对存储的许多并发访问进行了优化(现代 ssd 可以有数万个并发访问)。
    • @EOF - 我在 iMac(闪存)上计时了多线程读取,但没有衡量任何好处。如果这很重要,我已经使用 Go 的并发性完成了。
    • Go 中 MacOS 上未指定读取并发的单个失败测试与 C++ 中要在 LInux 上读取的数十万个文件有何关系?
    • @EOF - 撤回。但是我的其他建议有效吗?
    猜你喜欢
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多