javac有多聪明?

时间:2013-01-02 09:24:39

标签: java compiler-construction javac

如果我用javac编译下面的代码,编译器是否足够聪明,只能评估一次codeInput.length(),或者我可能通过首先将迭代器变量影响的评估得到更快的程序? / p>

// edit: The first character must be an upper case letter, the next four must be 
// edit: numeric and there must be exactly five characters in total in a product
// edit: code.

for (int i = 1; i < 5; i++)
    if (codeInput.length() != 5 || codeInput.charAt(i) < '0' 
        || codeInput.charAt(i) > '9' || codeInput.charAt(0) < 'A'
        || codeInput.charAt(0) > 'Z')
        {if (verbose) System.out.println("Sorry, I don't understand! 
            Use product codes only."); return true;}

6 个答案:

答案 0 :(得分:3)

作为一项规则;你应该编写最简单,最清晰的代码,这样做会很好。通常,性能问题会使代码更清晰,这一点更为重要。在你的例子中,我会这样写。

static final Pattern CODE = Pattern.compile("[A-Z][0-9]{4}");

if (!CODE.matcher(codeInput).matches()) {
    if (verbose) 
        System.out.println("Sorry, I don't understand! Use product codes only.");
    return true;
}

即使这稍微慢一点,维护恕我直言也更容易和更容易。


javac旁边没有优化,它们几乎全部由JIT在运行时执行。

  

如果我用javac编译以下代码,编译器是否足够聪明,只能评估一次codeInput.length(),

不,但是,

  

或者我是否可以通过首先使用迭代器变量影响的评估来获得更快的程序?

与您不同的是,您会注意到差异,因为如果代码运行得足够长,JIT将会启动,并且方法将被内联,从而无需局部变量。

答案 1 :(得分:2)

您的第一个目标应该是代码的简单性和可读性。对于一个像你提出的问题一样简单的问题,你真的必须不遗余力地把它变成一个性能瓶颈。最简洁,最灵活的字符串验证方法是正则表达式:

boolean valid = codeInput.matches("[A-Z][0-9]{4}");
if (!valid && verbose) 
   System.out.println("Sorry, I don't understand! Use product codes only.");
return !valid;

如果你真的想从中获得最后一点性能,你可以预编译正则表达式:

static final Pattern productCodeRegex = Pattern.compile("[A-Z][0-9]{4}");
boolean validate(String codeInput) {
  boolean valid = productCodeRegex.matcher(codeInput).matches();
  ...
}

答案 2 :(得分:1)

是的,它会在每次迭代中评估codeInput.length()。如果codeInput是最终的,或者一次只能访问一个线程,则编译器可以进行优化。否则codeInput可以被当前线程不会注意到的另一个线程更改。

因此,程序员决定将codeInput.length()转移到循环之外,并利用提升的性能。

答案 3 :(得分:1)

编译器必须创建在每次迭代时评估codeInput.length()的代码,因为它无法知道它是否会在每次迭代时返回相同的值。你知道,因为你的课程是如何工作的。但是编译器不知道它并且必须假设codeInput发生了变化(例如由于charAt()的调用)。

是的:您可以通过评估codeInput.length()一次并将结果分配给局部变量来获得一些性能。

答案 4 :(得分:1)

这里不是编译器,而是 JVM

现代JVM都有JIT(即时),这意味着代码将在旅途中进行优化。

不要试图在Java中优化代码,只需编写正确的代码。 JVM将为您完成剩下的工作。

至于您的特定代码段,如果您使用的是Guava,则可以使用CharMatcher效果良好:

private static final CharMatcher PRODUCT_CHARS
    = CharMatcher.inRange('A', 'Z')
        .or(CharMatcher.inRange('a', 'z'))
        .or(CharMatcher.inRange('0', '9'))
        .precompute();

// In the verification method:
if (!(codeInput.length() == 5 && PRODUCT_CHARS.matchesAllOf(codeInput)))
    badProductCode();

答案 5 :(得分:0)

Marko的回答显然是这个问题中编程努力的最佳答案。

然而,使用这个作为一个通用的例子可以推断到没有这种更好的解决方案的情境(如regexp或CharMatcher),作为风格和实践的问题,我使用并推荐更简单的表达式,捕获什么会否则使用局部变量重复方法调用结果。

我断言这可以提高清晰度,因为我们可以命名局部变量,并允许我们在最合理的位置找到每个更简单的代码片段(同时也允许JVM优化它真正擅长的一件事,即局部变量用法)。

你会注意到这个转换版本,我们在循环外测试firstChar,而不是在循环内重复测试;与.length()相同。我首先要说的是,这在逻辑上更正确的编程(对于其他读者而言,为什么这可能在循环中反复进行)会让人感到困惑,而且只有它会有更好的性能。

虽然循环的这种代码运动不会对这个简单示例的性能产生重大影响(仅使用5次迭代,并且有更好的解决方案使用为我们循环的库),但在一般情况下和其他情况下,我建议这是最佳实践,更易读,更清洁(同时也是以性能为导向)。

另请注意,我首先测试长度,以便已知存在.charAt(0),即长度> 1。 0,意味着我们将reportError()在零长度字符串上,而不是抛出一些IndexOutOfBounds。这是使用更简单的表达式如何允许编程逻辑的高级排序的另一个例子。

这种KIS(简单易用)样式也使调试变得更容易,因为可以轻松地观察这些局部变量。我认为它对维护者来说也更具可读性。

此外,将常数5限制在一个位置可以简化维护。

public static final int expectedLength = 5;

...

if ( codeInput.length() != expectedLength )
    return reportError ();

char firstChar = codeInput.charAt(0);
if ( firstChar < 'A' || firstChar > 'Z' )
    return reportError (); 

for (int i = 1; i < expectedLength; i++) {
    char nextChar = codeInput.charAt(i);
    if ( nextChar < '0' || nextChar > '9' )
        return reportError ();
}

...

private static boolean reportError () {
    if (verbose) 
        System.out.println("Sorry, I don't understand!\nUse product codes only."); 
    return true;
}