【问题标题】:Optimize a Regex优化正则表达式
【发布时间】:2012-01-30 02:22:09
【问题描述】:

我正在使用以下代码从连接到大型 ISP 网络(我指的是数万个路由器)的路由器中丢弃不受支持的物理接口/子接口:

private final static Pattern INTERFACES_TO_FILTER = 
   Pattern.compile("unrouted VLAN|GigabitEthernet.+-mpls layer|FastEthernet.+-802\\.1Q vLAN subif"); 

// Simplification
List<String> interfaces;
// lots of irrelevant code to query the routers 

for (String intf : interfaces) {
   if (INTERFACES_TO_FILTER.matcher(intf).find()) {
      // code to prevent the interface from being used
   } 
}

这个想法是丢弃以下条目:

  • GigabitEthernet2/11.2000 的未路由 VLAN 2000
  • GigabitEthernet1/2-mpls 层
  • FastEthernet6/0/3.2000-802.1Q vLAN subif

这段代码在大量接口(一些路由器有 50k+ 子接口)上经常被命中(每分钟几次),缓存也没有太大帮助,因为新的子接口经常被配置/丢弃。该计划是优化正则表达式,以便该过程完成得更快一点(每一纳秒都很重要)。各位大神能指教一下吗?

注意: mpls layer802.1Q 支持其他类型的接口,unrouted VLANs 不支持。

【问题讨论】:

  • 为什么不先隔离应该匹配的字符串并检查一下
  • 您是否尝试过仅使用 String.contains 而不是正则表达式?
  • 您提供的那些条目是整行还是取自行中间的一些单词?
  • 我不知道,但很容易测试它是否值得。您也可以使用 indexOf 并仅测试在“GigabitEthernet”+ 15 的索引之后找到“-mpls ayer”(15 是“GigabitEthernet”的长度)
  • 那么matches() 也不能正常工作这不会在每个位置都尝试(仅尝试 1 个匹配而不是 intf.length()

标签: java regex optimization


【解决方案1】:

我正在回答我自己的问题以供进一步参考,尽管功劳归于@piotrekkr,因为他是指路的人。我还要感谢@JB 和@ratchet。我最终使用了matches(),并且使用indexOf 和几个contains 的逻辑几乎一样快(这对我来说是个新闻,我一直认为单个正则表达式会比多次调用contains 更快)。

这是一个速度快几倍的解决方案(根据分析器,Matcher 类方法所花费的时间大约减少了 7 倍):

^(?:unrouted VLAN.++|GigabitEthernet.+?-mpls layer|FastEthernet.+?-802\\.1Q vLAN subif)$

【讨论】:

    【解决方案2】:

    如果您必须为此使用正则表达式,请尝试更改为这个:

    ^(?:unrouted VLAN)|(?:GigabitEthernet.+?-mpls layer)|(?:FastEthernet.+?-802\.1Q vLAN subif)
    

    ^ 使引擎从字符串的开头匹配,而不是字符串中的任何地方

    .+? 使 + 不贪婪

    (?:...) 使() 非捕获组

    【讨论】:

      【解决方案3】:

      如果您的问题是要搜索大量长字符串常量,我建议您使用标准 C 工具“lex”的 Java 模拟。

      快速谷歌搜索将我带到JFlex。我没有使用过这个特定的工具,可能还有其他可用的工具,但这是我会寻找的那种工具的一个例子。

      【讨论】:

      • 那么 lex 最终会为他创建一个 FSM 并且嗯.. 正则表达式已经在这样做了..
      • 好点。我想使用 lex 工具有两个原因:(1)我希望该工具在优化 FSM 方面比正则表达式做得更好(因为它可以花更多时间进行优化)。 (2) 与包含许多 |s 的长正则表达式相比,lex 文件为我提供了一种更易读的方式来组织我的搜索字符串。
      【解决方案4】:

      有一些字符串搜索算法允许您尝试在长度为 n 的字符串中搜索 k 个字符串,这比明显的 O(n*k) 成本要便宜。

      他们通常将滚动哈希与您单词的现有哈希列表进行比较。一个典型的例子是Rabin-Karp algorithm。 wiki 页面甚至有一个关于此的部分。该原理还有更高级的版本,但原理很容易理解。

      不知道Java中是否已经有库可以做到这一点(我想是这样),但这就是我要尝试的——尽管这里有5个字符串相当小(而且不同的大小也使它更复杂)。所以最好检查一个好的 KMP 字符串搜索是否不是更快 - 我认为这确实是迄今为止最好的解决方案(默认的 java api 使用天真的字符串搜索,所以使用 lib)

      关于您的正则表达式:为性能关键的搜索代码回溯正则表达式实现?我怀疑这是个好主意。

      PS:如果你为你的问题发布一个测试集和一个测试工具,那么好人很可能会看到他们能打败最喜欢的人 - 以前曾经工作过......人性是如此容易欺骗:)

      【讨论】:

      • 谢谢@Voo。我会深入研究它。至于测试工具,不确定我能做什么,也许在这些树字符串上迭代 5.000.000 次并使用System.nanotime():D 记录时间。我不擅长基准哈哈。如果我直到明天才对它感到满意,我将使用测试更新代码:D。
      • @Anthony 是的,用 java 编写基准测试并不容易。 caliper 应该让它更容易,但我没有使用它。如果您想要一个合理的基准,This 和 Cliff 的presentations 之一应该可以帮助您。
      • 对于一般的线束,我建议:由测试候选人实现的接口boolean valid(String s),然后您只需检查一些典型的输入字符串(并检查结果!)。最简单的是从您单独提供的文件中读取输入 - 毕竟应该可以表示您的常用数据,因此我们不能只在那里生成随机字符串。一般来说:永远不要优化你无法衡量的东西所以这真的应该是你做的第一件事。别担心,如果你发布它,我们会在你的第一个版本中指出一些问题;-)
      • 哈哈哈...这就是我不编写基准测试的原因...我正在使用分析器来测量我自己的代码:D。
      • @Anthony 这也行得通,尽管根据我的经验比较大型应用程序的运行是相当有问题的——即相同的代码,相同的数据在一次调用中运行速度会快 20% 或慢 20% -这就是 JITing、OSR 等的问题。但是对于较大的差异,使用分析器应该会给出一个准确的图片。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-09
      相关资源
      最近更新 更多