【问题标题】:Performance on a lookup array in CC 中查找数组的性能
【发布时间】:2015-01-25 15:53:17
【问题描述】:

我必须遍历一个包含大约 100M 行的日志文件。我必须为多个日志执行此操作。平均行长为 110 个字符。

目前我正在遍历可能匹配的列表。我想知道是否有更好的方法来做到这一点?

char *in_array(char *car) {
    // longer list than this...
    char *carlist[] =
    { 
        "Avalon",
        "Azera",
        "Cayenne",
        "Civic",
        "Corolla",
        "Elantra",
        "F-150",
        "Hilux",
        "Lexus LS",
        "Rav 4",
        "Sienna",
        // etc...
    };  

    char *match;
    int i;
    int n = sizeof(carlist)/sizeof(carlist[0]);

    for(i = 0; i < n; i++)
    {
        match = strstr(car, carlist[i]);
        if(match != NULL)
        {
            return strdup(match);
        } 
    }   

    return strdup("No match");
}

【问题讨论】:

  • 你可以使用bsearch()
  • @iharob: bsearch() 不起作用,因为它是子字符串比较。这不是具有给定术语的数组查找。
  • @iharob 使用 bsearch,我是否能够从匹配而不是从数组中获取返回值?例如,返回Elantra13,而不是Elantra
  • 考虑使用数据库?
  • 也许没有(不必要的)strdup?

标签: c arrays performance lookup


【解决方案1】:

DFA(Deterministic Finite Automaton,复数 Automata)可以将字符串与多个模式进行匹配,比单独顺序测试每个模式要快得多。

解析器生成器擅长自动构建 DFA 表。

不利的一面是,当模式列表发生变化时,必须重新构建 DFA……但由于您的模式列表当前被硬编码为数组,这对您来说应该没有问题。

由于您使用的是 C,flex 将是一个很好的工具。

【讨论】:

  • 类似的技术可以用在像Java/C# 这样的语言中吗 - 有没有可以学习的教程?我对这个问题不感兴趣,但你的回答让我感兴趣。
  • 注意术语:flex 是扫描仪生成器。 bison 是对应的解析器生成器。
  • @Gene:是的。我想编译器生成器的两个组件(扫描器/词法分析器生成器和解析器/语法生成器)都会大量使用 DFA。
  • @al-Acme:Java 有 JFlex;我不知道C#。 (当然,您可以在 C 中编译 flex 函数,然后 P/Invoke 它。)对于更动态的方法,请查看en.wikipedia.org/wiki/…
  • 很抱歉最初没有拼出首字母缩略词。好的搜索短语应该是“使用 DFA 进行并行字符串匹配”
【解决方案2】:

有几种方法可以改进这种蛮力搜索...

1) 并行性,使用 OpenCL/CUDA(类似 C 的语言)并在 GPU 上执行。每个关键字使用一个 GPU 线程。将 100M 行文件的部分读入 GPU 内存并并行搜索。使用 OpenCL,它也可以在多核硬件上运行。

2) 改进算法。使用哈希查找,计算关键字的 CRC32 并将它们存储在哈希表中,并将日志文件记录中的哈希令牌与哈希表中的条目进行比较。

【讨论】:

  • 谢谢。我喜欢将关键字存储到哈希表中的想法。
  • 这是基于数据库的表哈希连接建模
【解决方案3】:

您的扫描中的瓶颈似乎很可能是从日志文件中读取数据的成本;几乎所有非天真的搜索算法都应该比磁盘寻道执行得更快。

同样,OP 中的蛮力算法不必要地占用 CPU 资源,尤其是在单词列表很长的情况下。 Aho-Corasick string matching 是一个相当简单且高效的算法,其实现很容易被发现。

如果单词列表是静态的,您可以通过预先计算 Aho-Corasick 的转换表来稍微加快速度,但在这种情况下,与读取数据的成本相比,差异可以忽略不计。

【讨论】:

  • 我想这是一种更有效的方式来构建与从 flex... 获得的相同的 DFA...如果搜索列表在运行时可能会发生变化,这绝对是有益的。另一方面,它排除了任何比普通字符串更复杂的模式。解决手头特定问题的绝佳选择。
  • @BenVoigt 它通常是比 flex 产生的“更好”的 DFA,因为与 flex 生成的扫描仪不同,它永远不需要备份。如果您希望找到很多“误报”前缀,那将非常重要。 (您可以使用 flex 生成这样的 DFA,但您基本上需要生成 Aho-Corasick 机器,然后将其转换回大量模式和开始条件;它会变得非常混乱。)正如我所说,如果搜索列表在编译时是已知的,您可以预先生成机器,我希望它会比 flex 扫描仪更快。
  • Flex 生成回溯的扫描仪?我将不得不寻找确认,因为 DFA 永远不应该回溯(这是 DFA 与 NFA 的定义特征)
  • @BenVoigt:Flex 始终返回从当前扫描点开始的最长匹配令牌。如果某些不成功的模式比最长的成功匹配长,那么 flex 必须备份到最长成功匹配的末尾。如果您正在搜索关键字,则默认规则(匹配单个字符)在大多数情况下会成功匹配,因此任何超过一个字符的目标前缀最终都会备份。它从不备份在单个令牌扫描期间,仅在结束时备份。
  • 来自手册:'消除关键字列表中的回溯也可以使用“catch-all”规则来完成:''无论如何,因为在接受令牌之后才会发生回溯,对于手头的问题,它永远不会发生,因为处理在第一场比赛结束(并从下一个干草堆重新开始)。
【解决方案4】:

假设您的所有车名都在单词的开头匹配,您可以解析每一行并尝试匹配每个单词边界上的数组(使用bsearch())。尽管您需要对其进行测量,但这可能会更快。

static int
compare_car(const void *key, const void *elem)
{
    char *car = (char *)key;
    char *text = * (char **)elem;
    int len = strlen(key);
    return strncmp(car, text, len);
}

char *in_array(char *car) {
    // longer list than this... NEEDS TO BE SORTED
    char *carlist[] =
    { 
        "Avalon",
        "Azera",
        "Cayenne",
        "Civic",
        "Corolla",
        "Elantra",
        "F-150",
        "Hilux",
        "Lexus LS",
        "Rav 4",
        "Sienna",
        // etc...
    };  

    char *match;
    int i;
    int n = sizeof(carlist)/sizeof(carlist[0]);
    int len = strlen(car);
    bool inword = FALSE;

    for (i = 0; i < len; i++)
    {
        if (inword)
        {
            inword = isalnum(car[i]);
        }
        else
        {
            if (isalnum(car[i]))
            {
                inword = true;
                match = bsearch(car + i, carlist, n, sizeof(char*), compare_car);
                if (match != NULL)
                {
                    return strdup(match);
                }
            }
        }
    }

    return strdup("No match");
}

【讨论】:

    猜你喜欢
    • 2011-12-25
    • 2016-08-21
    • 2018-12-12
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多