我的REST服务运行负载很重,这意味着每天大约有数百万次读取通话。我的REST服务将基于userID从数据库进行查找,并检索与该userID相对应的一些列。
所以我目前在我的代码中看到了高性能问题。我怀疑下面的方法将是我应该首先开始优化的方法之一。
以下方法将接受attributeName
,然后根据它将使用正则表达式给我匹配。
我们举个例子 - 如果attrName
是technology.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)";
我只是想看看,这里是否有一些性能问题使用上面的正则表达式?如果是的话,那么再次重写这个东西的最佳方法是什么?记住性能问题?
感谢您的帮助。
答案 0 :(得分:3)
我用caliper来测试这个,结果是:如果你在每个方法调用之前编译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 ===
答案 1 :(得分: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
。
答案 2 :(得分:2)
有什么理由不能将正则表达式保存为Pattern ratter而不是字符串?如果正则表达式永远不会改变,那么每次使用它时都会浪费大量时间重新编译正则表达式。对于这样一个简单的模式,编译正则表达式可能比实际匹配它需要更多的时间。
至于正则表达式本身,我会推荐一些变化。这些更改将使正则表达式略微提高效率,但可能不足以引起注意。目的是使其更加健壮。
foo_technology.profile
或technology.profile_bar
等字符串产生误报。我相信你知道在你的情况下会发生什么样的事情,但为什么要避免这么容易避免的最小风险?
static final Pattern VALID_DOMAIN_PATTERN = Pattern.compile(
"\\b(?:technology|computer|sdc|adj|wdc|pp|stub)\\.(?:profile|preference|experience|behavioral)\\b");