多正则表达式匹配 (Multiple Regular Expression Matching) 中的动态 DFA 算法

前一段时间,在将 多正则表达式匹配工具 用于数十万任意的正则表达式时,以前一直担心的问题终于出现了:NFA 转化 DFA 时的指数爆炸,那样的 DFA 根本创建不出来,因为那些正则表达式之间有不可预料的各种交集!
这个问题对我打击很大,我甚至顿时觉得 多正则表达式匹配工具 完全是个废柴,最多,是个玩具!但是,只有挑战,才能激励人的斗志,挖掘人的潜能。我想起了曾经对之不屑一顾的动态 DFA 匹配算法,之前我在研究 RE2 时知道,RE2 在动态 DFA 的内存用量达到限定时,会抛弃已经创建整个动态 DFA,因为 DFA
的状态图比较复杂,节点之间互相引用,无法象普通的 Cache 一样部分的进行 Swap ,直觉上对它就没有好印象。

但是碰到组合爆炸这个魔咒,只有尝试一下,就先用 RE2 中的
set
试一下,先从那数十万正则表达式中任选一万个,re2 执行倒是很快,但立马就提示 DFA Out Of Memory 了,然后我尝试将 re2 的内存设到 8G,还是不行,浪费若干脑细胞,最后才惊奇地发现,Re2 的内存限制用的是 int 来表示的,而 int 的事实标准是32 bit,最大只能是 2G-1!设成 2G-1之后,re2 运行正常,性能还不错。

于是我开始加码,将所有正则表达式都扔进去,果然,re2 崩了!

虽然如此,但是至少表明,动态 DFA 解决该问题是可行的,我开始实现我的计划。一切都很顺利:

之前我实现其他算法时(r1303),扩展了自动机的字符集,而没有在自动机的状态上打专用 Tag,这个决定真是明智之举,在这里又用上了:

ch=256 表示括号捕获 用于我最早实现 One Pass 正则表达式的 DFA 算法
ch=257 表示全匹配 之前在一遍扫描中捕获所有括号时,发现因括号位置太靠前而导致严重的性能问题,
就实现了两遍扫描捕获括号,第一遍只看全匹配,获得正则表达式ID,
第二遍专门针对具体正则ID捕获括号,性能要好的多
ch=258 表示伪Epsilon转移 这是实现动态 DFA 匹配的一个根基,不过这个伪 Epsilon 转移只起到一个链表的作用,将所有子 DFA 的根(初始状态)链起来

如果一切都照抄 re2 的实现,那不过是无聊的体力劳动。我的动态 DFA 实现,完全建立在我之前的各种自动机算法之上,有了这个根基,我的实现相比 re2,有很多优势:

  1. 系统:如同一套数学理论,有定义、公理、定理,任何新的推论,都建立在之前已经确立的坚实的基础之上
  2. 简单:因为已经有了坚实的基础,并不需要引入多少全新的东西,就能解决新的问题
  3. 可靠:不需要验证已有的代码,只要证明新加入的代码是正确的,整个系统就是正确可靠的
  4. 高效:已有的代码已足够高效,只要新的代码没有冗余计算,最终的算法必然是高效的
事实证明了这些:专门用于实现动态 DFA 的代码也就 500 行左右,包含了算法的所有细节:
  1. 一个基于已有 DenseDFA 的 DynDFA 基础类
  2. 用于 subset 查找的专用 hash table 实现,包括 find, insert, hash resize, hash function, hash equal, hash cache, …
  3. 动态 DFA 的 warm up ,将靠近完整 DFA 的根(初始状态) 附近的状态构造出来作为热点,re2 中则完全没有考虑 warm up
  4. 动态 DFA 内存超限时,只抛弃不在 warm up 集合的那些状态,而不像 re2 一样抛弃整个动态 DFA
  5. 动态 DFA 的基础 NFA 只是简单地用 ch=258 将所有子 DFA 并到一起,而且每个子 DFA 都是最小化的,同时所有的子 DFA 共享了相同的尾
  6. 动态 DFA 直接引用作为捕获括号(submatch) 的子 DFA
最后的测试表明,我的算法比 RE2 要快 4~7 倍,在达到这个性能的同时,内存用量还要小 5~8 倍,并且,对于让 re2 崩掉的那几十万个正则表达式,运行完全正常,同时,还能捕获括号(submatch)。
使用:在 多正则表达式匹配工具 中,命令行参数 -D 表示生成的自动机匹配时用动态 DFA。

 

作者:
该日志由 csdn-whinah 于2014年01月05日发表在自动机分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
转载请注明: 多正则表达式匹配 (Multiple Regular Expression Matching) 中的动态 DFA 算法
标签:
【上一篇】
【下一篇】

您可能感兴趣的文章:

3 个回复

  1. whppc说道:

    很感兴趣这个东西?请问我如何做评估呢?另外是否和Intel的hyperscan做过比较?

    • rockeet说道:

      1. 下载包中有 regex_maxmatch ,可以用作 benchmark:先用 regex_build.exe 生成自动机,再运行regex_maxmatch
      2. 正则数目超过300个时,hyperscan就不行了
      详细使用方法请参考:规则引擎建库工具

      • whppc说道:

        多谢,我测试了一下,由于我的regex都是以.*开头的在IDS中实际使用的,得到的结果不是太好。而hyperscan则可以在1秒内compile上万条.*开头的pcre。不知以后是否考虑对这种全部.*开头的情况做优化?
        回头再试试AC的情况。

发表评论

您必须 登录 后才能发表评论。