并行正则表达式与 NFA 与 DFA 匹配?哪个更快?

Parallel regex matching with NFA vs DFA? Which one is faster?

我正在阅读有关 NFA 和 DFA 的内容,似乎实现正则表达式匹配器的最流行和最快的方法是从正则表达式创建 NFA,将其转换为 DFA,最小化 DFA,以任何语言实现并使用它.

DFA 比 NFA 更好,因为它只有一个输入转换,而 NFA 可以有多个。因此,DFA 只有一条路可走,而 NFA 有很多。

但是,这是我不明白的地方。为什么我们必须跟踪 NFA 状态并返回到它们,这会减慢我们的速度,我们能否在遇到多个状态的输入时分成不同的线程并并行计算每条路径?会不会比 DFA 更快?或者我遗漏了什么?

一般来说,DFA 更快,但 NFA 更紧凑。 NFA 与正则表达式的大小成正比。 (非正式证明:正则表达式语法中的每个运算符节点只是向 NFA 图中添加一个新节点。)因为 DFA 是由 NFA 状态集的子集形成的,所以在某些情况下它可能非常大。在最坏的情况下,DFA 的正则表达式呈指数增长 w.r.t。这方面的一个示例是 (a|b)(a|b)(a|b)(a|b)...(a|b) 形式的表达式,其中有 N (a|b) 个单元转换为大小为 O(2**N) 的 DFA。它包含通过 ab 的所有组合的唯一状态的转换。在模拟等效 NFA 所需的数据结构适合缓存的情况下,退化 DFA 可能会超过 CPU 缓存的大小。

由于需要额外的步骤,DFA 的前期成本要高一些。因此需要权衡取舍:NFA 模拟器是否会处理足够的数据以证明构建 DFA 是合理的。

NFA 模拟可以完全避免触及根本不适用于输入的正则表达式部分。例如,假设一个正则表达式具有 R1|R2 的形式,其中 R1 非常简单和小,而 R2 是一个巨大而复杂的野兽。假设输入通常只与 R1 匹配,而 R2 几乎不适用(例如,由于某些前缀不匹配,根本没有任何部分)。这会影响权衡:编译为 DFA 意味着编译所有内容,简单的 R1 部分和巨大的 R2 部分。

最后,实现不一定是严格的 NFA 或 DFA。 NFA 模拟器 can cache the state 设置它计算的内容。这些缓存状态等同于 DFA 状态,并提供与 DFA 编译类似的好处。你可以认为这是"JIT for the NFA"。可以将缓存修剪到某个固定大小,并遵循替换策略,以便可以在更少的内存中处理其完整 DFA 较大的表达式(如果数据在缓存中显示良好的引用位置,则速度几乎一样快) .