Java控制结构中的自动编译器优化?

Java控制结构中的自动编译器优化?,java,compiler-construction,for-loop,Java,Compiler Construction,For Loop,我有一个关于Sun的JDK中提供的Java编译器有多智能的快速问题。具体来说,提前评估for循环的条件部分中出现的任何函数,而不是在循环的每次迭代中评估它们,这是否足够聪明 例如,考虑下面的代码。 // Array of doubles to "hold" the array. private double matrix[][]; public int getCols() { // Compute the number of columns in a matrix. } public

我有一个关于Sun的JDK中提供的Java编译器有多智能的快速问题。具体来说,提前评估for循环的条件部分中出现的任何函数,而不是在循环的每次迭代中评估它们,这是否足够聪明

例如,考虑下面的代码。

// Array of doubles to "hold" the array.
private double matrix[][];

public int getCols() {
    // Compute the number of columns in a matrix.
}

public int getRows() { 
    // Compute the number of rows in a matrix.
}

// Compute the sum of all elements in the matrix.
public double sum() {

    double result = 0;

    for (int r = 0; r < getRows(); r++) {
        for (int c = 0; c < getCols(); c++) {
            result += this.matrix[r][c];
        }
    }

    return result;
}
显然,我可以修改sum方法,通过将其更改为

public double sum() {

    double result = 0;
    int numRows = getRows();
    int numCols = getCols();

    for (int r = 0; r < numRows; r++) {
        for (int c = 0; c < numCols; c++) {
            result += this.matrix[r][c];
        }
    }

    return result;
}
然而,我想知道编译器是否足够聪明,能够自己对它们进行预评估。也就是说,它会自动发现,提前计算条件中出现的任何函数比在每次迭代中计算它们更便宜吗


谢谢

我不认为编译器会这么聪明。它无法知道getRows方法是如何实现的。如果每次调用时它都返回不同的值呢

所以,底线是。代码的第二个版本要好得多。不仅仅是从性能的角度。它更具可读性,我相信您在编码时应该更喜欢这种方法


我允许自己写入for循环的唯一方法调用是数组的length方法调用。数组不是实类,它的length方法也不是实方法,所以它的调用不会影响性能

我不认为编译器会这么聪明。它无法知道getRows方法是如何实现的。如果每次调用时它都返回不同的值呢

所以,底线是。代码的第二个版本要好得多。不仅仅是从性能的角度。它更具可读性,我相信您在编码时应该更喜欢这种方法


我允许自己写入for循环的唯一方法调用是数组的length方法调用。数组不是实类,它的length方法也不是实方法,所以它的调用不会影响性能

一般来说,这样的优化是错误的。这些方法是虚拟的,因此它们可能在子类中被更改以执行完全不同的操作,例如返回随机数,并且可能很难静态地证明是的,它需要证明它们在每次迭代中返回相同的值,即使它们是最终的。事实上,也许他们根本不会这么做——想想另一个线程会并行改变行/列的数量——是的,在这种情况下,你会遇到很多其他问题,但这仍然需要考虑


除此之外,编译器不需要优化任何东西:在运行时,JIT编译器可以进行更多优化。例如,它可以生成带有虚拟调用内联线的代码,并且至少在它们无条件返回常量的情况下,它可以根据代码排除错误。但是,如果不能更改语义,那么它也不会这样做。如果这真的很重要,你自己做吧。无论哪种方式,都要检查它是否是一个瓶颈。

一般来说,这样的优化是错误的。这些方法是虚拟的,因此它们可能在子类中被更改以执行完全不同的操作,例如返回随机数,并且可能很难静态地证明是的,它需要证明它们在每次迭代中返回相同的值,即使它们是最终的。事实上,也许他们根本不会这么做——想想另一个线程会并行改变行/列的数量——是的,在这种情况下,你会遇到很多其他问题,但这仍然需要考虑


除此之外,编译器不需要优化任何东西:在运行时,JIT编译器可以进行更多优化。例如,它可以生成带有虚拟调用内联线的代码,并且至少在它们无条件返回常量的情况下,它可以根据代码排除错误。但是,如果不能更改语义,那么它也不会这样做。如果这真的很重要,你自己做吧。不管是哪种方式,都要检查它是否是一个瓶颈。

如果编译器只对这些表达式求值一次,那将是非常可怕的,因为您可能需要对它们求值不止一次。 但您可以编写一个小测试:

public class Test {

    private static int number = 5;

    public static void main(String[] args) {
        for(int i = 0; i < end(); i++) {
            System.out.println(i);
        }
    }

    public static int end() {
        return number--;
    }

}

这表明编译器每次都对表达式求值。

如果编译器只对这些表达式求值一次,那将是非常可怕的,因为您可能需要多次对它们求值。 但您可以编写一个小测试:

public class Test {

    private static int number = 5;

    public static void main(String[] args) {
        for(int i = 0; i < end(); i++) {
            System.out.println(i);
        }
    }

    public static int end() {
        return number--;
    }

}

这表明编译器每次都对表达式求值。

编译器几乎不进行优化,但是Sun/Oracle编译器足够聪明,最多可以内联两个虚拟方法。取决于getter的复杂性,它可能不会有什么不同

它应该足够聪明,使您的循环与

for (int r = 0, rmax=getRows(), cmax=getCols(); r < rmax; r++) {
    double[] cs = this.matrix[r]
    for (int c = 0; c < cmax-1; c+=2) {
        result += cs[c] + cs[c+1];
    }
    if (cmax % 2 == 1)
        result += cs[cmax-1];
}
我已经看到它循环展开代码,以便在一个循环中执行两个循环

您可能对下载调试程序感兴趣
OpenJDK版本并使用-XX:+PrintAssembly编译器几乎没有进行任何优化,但是Sun/Oracle编译器足够智能,最多可以内联两个虚拟方法。取决于getter的复杂性,它可能不会有什么不同

它应该足够聪明,使您的循环与

for (int r = 0, rmax=getRows(), cmax=getCols(); r < rmax; r++) {
    double[] cs = this.matrix[r]
    for (int c = 0; c < cmax-1; c+=2) {
        result += cs[c] + cs[c+1];
    }
    if (cmax % 2 == 1)
        result += cs[cmax-1];
}
我已经看到它循环展开代码,以便在一个循环中执行两个循环

您可能对下载OpenJDK的调试版本感兴趣,并使用-XX:+PrintAssembly

javac不会优化这些东西。JVM确实如此

这取决于getRows和getCols中的内容。如果它们非常简单,几乎可以肯定它们将由JVM内联;然后JVM可以进一步优化,调用它们一次并计算结果,前提是它可以得出值不变的结论

例如,下面的代码

for(int i=0; i<string.length(); i++)
    char c = string.charAt(i);
将由JVM进行积极优化,我们的任何手动优化都无法击败它。

javac不会优化这些东西。JVM确实如此

这取决于getRows和getCols中的内容。如果它们非常简单,几乎可以肯定它们将由JVM内联;然后JVM可以进一步优化,调用它们一次并计算结果,前提是它可以得出值不变的结论

例如,下面的代码

for(int i=0; i<string.length(); i++)
    char c = string.charAt(i);

JVM将积极地进行优化,我们的手动优化无法击败它。

我怀疑这一点,因为推断状态依赖关系将非常复杂,几乎没有什么好处,但为什么不使用数组的长度属性呢?您当前的设置有点脆弱,因为它隐式地假设所有行具有相同的列数。我对此表示怀疑,因为推断状态依赖关系将非常复杂,几乎没有什么好处,但是为什么不使用数组的length属性呢?您当前的设置有点脆弱,因为它隐式地假设所有行具有相同的列数。+1:我同意,但这不是一个方法它是一个属性-只有上帝知道为什么:-+1:我同意,但它不是一个方法它是一个属性-只有上帝知道为什么:-+1有这么多优点。我只想指出,这种所谓的“优化”在许多语言中都是错误的,不仅仅是在Java中。基本上任何语言都有副作用。@delnan谢谢你。指出为什么期望编译器这样做通常不是一件合理的事情的一些原因是非常有帮助的。我只想指出,这种所谓的“优化”在许多语言中都是错误的,不仅仅是在Java中。基本上任何语言都有副作用。@delnan谢谢你。指出为什么期望编译器这样做通常不合理的一些原因非常有帮助。谢谢!关于使用-XX检查生成代码的提示将非常方便。谢谢!关于使用-XX检查生成代码的技巧将非常方便。