【问题标题】:Search for multiple words in very large text files (10 GB) using C++ the fastest way使用 C++ 以最快的方式在超大文本文件 (10 GB) 中搜索多个单词
【发布时间】:2011-10-03 09:16:19
【问题描述】:

我有这个程序,我必须在非常大的文本文件中搜索特定值及其行号,并且同一值可能会出现多次。

我尝试了一个简单的 C++ 程序,它逐行读取文本文件并使用 strstr 搜索值,但这需要很长的时间 gggggggggggggg

我也尝试使用 grep 使用系统命令,但仍然需要很多时间,不像以前那么长,但仍然太多时间。

我正在寻找一个可以用来加快搜索速度的库。 有什么帮助和建议吗?谢谢你:)

【问题讨论】:

  • 如果您正确编写代码,您将不会获得更快的速度。一个文件中 10GB 的文本非常多。此外,由于线的长度彼此不相等,并且存在获得例如的风险。一行 1Gb,你应该分块阅读。 (例如,1024 个字节,或者只要你的字符串是,记得还要检查块之间的出现。(想象你读了 3 个字节,你需要在 heabcjabc /b>)
  • 二进制读取+缓冲读取器或可管理大小的文件的内存映射段并在其上处理

标签: c++ unix search full-text-search large-files


【解决方案1】:

关于速度有两个问题:实际需要的时间 读取数据,以及搜索所需的时间。

一般来说,读取文件最快的方法是mmap它(或 Windows 下的等价物)。如果整个 文件不适合地址空间,但您在 标头;如果搜索是您在程序中所做的全部,则不应创建 任何问题。

更一般地说,如果速度是个问题,请避免使用getline string。阅读大块,并挑选线路(如char[]) 其中,无需复制,速度明显更快。 (作为一个简单的 妥协,当一条线穿过块边界时,您可能想要复制。 如果您正在处理 MB 或更大的块,这不应该太 经常;我在旧的 16 位机器上使用了这种技术,带有块 32KB,并且仍然获得了显着的性能提升。)

关于搜索,如果您正在搜索单个固定的 字符串(不是正则表达式或其他模式匹配),您可能 想试试BM搜索。如果您要搜索的字符串是 相当长,这可以使其他显着不同 搜索算法。 (我认为grep 的一些实现会 如果搜索模式实际上是一个固定字符串,则使用它,并且是 足够长的时间让它有所作为。)

【讨论】:

  • 如果进程是 32 位,则无法映射 10Gb。
  • @JeremyP 不。我正在混合GB和MB。您必须按段进行(当线穿过段边界时进行特殊处理)。
【解决方案2】:

使用多个线程。每个线程可以负责搜索文件的一部分。例如在 4 核机器上产生 12 个线程。第一个线程查看文件的前 8%,第二个线程查看文件的第二个 8%,依此类推。您需要调整每个内核的线程数以保持 cpu 的最大利用率。由于这是一个 I/O 绑定操作,您可能永远无法达到 100% 的 CPU 利用率。

向线程提供数据将成为使用这种设计的瓶颈。内存映射文件可能会有所帮助,但最终磁盘一次只能读取一个扇区。这将是您将难以解决的瓶颈。您可以考虑启动一个线程,它只会将所有数据读入内存并在数据加载时启动搜索线程。

【讨论】:

    【解决方案3】:

    由于文件是从头到尾搜索的连续野兽,您可能无法绕过,但您可以做一些事情。

    如果数据是静态的,您可以生成一个较小的查找文件(替代主文件的偏移量),如果相同的字符串重复多次使索引文件更小,这会很好。如果文件是动态的,您可能需要偶尔(离线)重新生成索引文件

    不是逐行读取,而是从文件中读取更大的块(例如几 MB)以加快 I/O。

    【讨论】:

      【解决方案4】:

      如果您想使用库,可以使用xapian。

      您可能还想在搜索之前尝试标记您的文本,我也建议您也尝试使用正则表达式,但如果您没有该文本的索引,这将需要很多时间,所以我肯定会建议你可以试试 xapian 或一些搜索引擎。

      【讨论】:

        【解决方案5】:

        如果您的大文本文件不经常更改,则创建一个带有表的数据库(例如 SQLite):

        create table word_line_numbers
          (word varchar(100), line_number integer);
        

        读取您的文件并在数据库中为每个单词插入一条记录,如下所示:

        insert into word_line_numbers(word, line_number) values ('foo', 13452);
        insert into word_line_numbers(word, line_number) values ('foo', 13421);
        insert into word_line_numbers(word, line_number) values ('bar', 1421);
        

        创建单词索引:

        create index wird_line_numbers_idx on word_line_numbers(word);
        

        然后你可以使用这个索引快速找到单词的行号:

        select line_number from word_line_numbers where word='foo';
        

        为了提高速度(因为数据库更小)和复杂性,您可以使用 2 个表:words(word_id integer primary key, word not null) 和 word_lines(word_id integer not null references words, line_number integer not null)。

        【讨论】:

          【解决方案6】:

          我会尝试首先将尽可能多的文件加载到 RAM 中(文件的内存映射是一个不错的选择),然后在多个处理器上同时搜索部分文件。您需要在缓冲区边界附近特别小心,以确保您没有遗漏任何单词。此外,您可能想尝试比典型的 strstr() 更有效的方法,请参阅这些:
          Boyer–Moore string search algorithm
          Knuth–Morris–Pratt algorithm

          【讨论】:

            猜你喜欢
            • 2013-07-05
            • 2013-09-04
            • 1970-01-01
            • 2023-03-15
            • 1970-01-01
            • 2016-08-01
            • 2011-10-28
            • 1970-01-01
            • 2012-02-03
            相关资源
            最近更新 更多