2

我的 REST 服务在重负载下运行,这意味着它每天会获得大约数百万次读取调用的大量流量。我的 REST 服务将根据用户 ID 从数据库中进行查找,并检索与该用户 ID 对应的几列列。

所以我目前在我的代码中看到了高性能问题。我怀疑下面的方法将是我首先应该开始优化的方法之一。

下面的方法将接受一个attributeName然后基于它会使用正则表达式给我匹配。

举个例子——如果attrNametechnology.profile.financial

那么下面的方法会将我返回为technology.profile. 这样它也适用于其他情况。

private String getAttrDomain(String attrName){
    Pattern r = Pattern.compile(CommonConstants.VALID_DOMAIN);
    Matcher m = r.matcher(attrName.toLowerCase());
    if (m.find()) {
      return m.group(0);
    }
    return null;
}

CommonConstants类文件中

String  VALID_DOMAIN = "(technology|computer|sdc|adj|wdc|pp|stub).(profile|preference|experience|behavioral)";

我只是想看看,这里是否可能存在一些性能问题或不使用上面的正则表达式?如果是,那么在牢记性能问题的情况下,再次重写这个东西的最佳方法是什么?

谢谢您的帮助。

4

3 回答 3

3

我用 caliper 测试了这个和这个,结果是:如果你在每个方法调用之前编译 Pattern,这将是最快的方法。

您的正则表达式方法是最快的方法,但是您需要做的唯一更改是预先计算 Pattern ,而不是每次:

 private static Pattern p = Pattern.compile(VALID_DOMAIN);

然后在你的方法中:

 Matcher matcher = pattern.matcher(input); ...

对于感兴趣的人,这是我用于卡尺的设置:--warmupMillis 10000 --runMillis 100

 package stackoverflow;

 import java.util.regex.Matcher;
 import java.util.regex.Pattern;

 import com.google.caliper.Param;
 import com.google.caliper.Runner;
 import com.google.caliper.SimpleBenchmark;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Iterables;

 public class RegexPerformance extends SimpleBenchmark {
      private static final String firstPart    = "technology|computer|sdc|adj|wdc|pp|stub";
      private static final String secondPart   = "profile|preference|experience|behavioral";
      private static final String VALID_DOMAIN = "(technology|computer|sdc|adj|wdc|pp|stub)\\.(profile|preference|experience|behavioral)";

      @Param({"technology.profile.financial", "computer.preference.test","sdc.experience.test"})
      private String input;

      public static void main(String[] args) {
           Runner.main(RegexPerformance.class, args);
      }

      public void timeRegexMatch(int reps){
          for(int i=0;i<reps;++i){
              regexMatch(input);
          }
      }


      public void timeGuavaMatch(int reps){
          for(int i=0;i<reps;++i){
              guavaMatch(input);
          }
      }

      public void timeRegexMatchOutsideMethod(int reps){
          for(int i=0;i<reps;++i){
              regexMatchOutsideMethod(input);
          }
      }


    public String regexMatch(String input){
        Pattern p = Pattern.compile(VALID_DOMAIN);
        Matcher m = p.matcher(input);
        if(m.find()) return m.group();
        return null;
    }

    public String regexMatchOutsideMethod(String input){
          Matcher matcher = pattern.matcher(input);
          if(matcher.find()) return matcher.group();
          return null;
    }

    public String guavaMatch(String input){
        Iterable<String> tokens = Splitter.on(".").omitEmptyStrings().split(input);
        String firstToken  = Iterables.get(tokens, 0);
        String secondToken = Iterables.get(tokens, 1);
        if( (firstPart.contains(firstToken) ) && (secondPart.contains(secondToken)) ){
            return firstToken+"."+secondToken;
        }
        return null;
    }
}

以及测试结果:

             RegexMatch technology.profile.financial 2980 ========================
             RegexMatch     computer.preference.test 2861 =======================
            RegexMatch           sdc.experience.test 3683 ==============================
RegexMatchOutsideMethod technology.profile.financial  179 =
RegexMatchOutsideMethod     computer.preference.test  227 =
RegexMatchOutsideMethod           sdc.experience.test  987 ========
             GuavaMatch technology.profile.financial  406 ===
             GuavaMatch     computer.preference.test  421 ===
            GuavaMatch           sdc.experience.test  382 ===
于 2013-04-20T08:33:47.313 回答
2

两个小点:

除了在函数之外编译表达式,如评论中所述,您可以进行()非捕获,因此不保存每个匹配的内容,即

String  VALID_DOMAIN = "(?:technology|computer|sdc|adj|wdc|pp|stub)\\.(?:profile|preference|experience|behavioral)";

如果有效域必须始终出现在属性名称的开头,您也许可以使用该lookingAt方法而不是find,因此匹配可能会更快失败,即

 if (m.lookingAt()) {

如果表达式是在函数之外编译的,你可以添加Pattern.CASE_INSENSITIVE这样,那么你就不必每次都调用toLowerCase()了。attrName

于 2013-04-20T09:16:39.530 回答
2

有什么理由不能将正则表达式保存为 Pattern ratter 而不是字符串?如果正则表达式永远不会改变,那么每次使用它时都会浪费大量时间重新编译正则表达式。对于这样一个简单的模式,编译正则表达式可能比实际匹配它花费更多的时间。

至于正则表达式本身,我建议进行一些更改。这些更改将使正则表达式更加高效,但可能不足以引起注意。目的是让它更健壮。

  • foo_technology.profile将其括在单词边界中以避免像or之类的字符串出现误报technology.profile_bar。我敢肯定你知道这种事情会发生在你的身上,但既然如此容易避免,为什么还要冒最小的风险呢?
  • 正如@plaix 建议的那样,避开点。
  • 使用非捕获组而不是捕获。(假设您真的不需要分解属性名称的各个组成部分。)

 

static final Pattern VALID_DOMAIN_PATTERN = Pattern.compile(
    "\\b(?:technology|computer|sdc|adj|wdc|pp|stub)\\.(?:profile|preference|experience|behavioral)\\b");
于 2013-04-20T09:24:49.917 回答