Автоматическая оптимизация компилятора в структурах управления Java? - PullRequest
1 голос
/ 22 июля 2011

У меня быстрый вопрос о том, насколько «умным» является компилятор Java, представленный в JDK от Sun.В частности, достаточно ли умен, чтобы оценивать какие-либо функции, появляющиеся в условной части цикла 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 () на обеспечить , чтобы getRows () и getCols () не оценивались на каждой итерации цикла, изменив его на

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;
}

Интересно, однако, достаточно ли умен компилятор, чтобы предварительно оценить их сам.То есть будет ли он автоматически определять, что вычислительно дешевле оценивать любые функции, появляющиеся в условных выражениях раньше, чем оценивать их на каждой итерации?

Спасибо!

Ответы [ 5 ]

5 голосов
/ 22 июля 2011

Обычно такая оптимизация будет неправильной .Методы являются виртуальными, поэтому они могут быть изменены в подклассе, чтобы сделать что-то совершенно другое (например, возвращение случайных чисел), и может быть трудно или невозможно статически доказать (да, это должно быть доказательством), что они возвращают одно и то жезначение на каждой итерации, даже если они были final.На самом деле, может быть, они вообще этого не делают (подумайте, что другой поток изменяет число строк / столбцов параллельно - да, у вас есть много других проблем в этом случае, но это все еще необходимо учитывать).

И кроме того, компилятору не нужно ничего оптимизировать: во время выполнения JIT-компилятор может оптимизировать гораздо больше.Например, он может генерировать код со встроенными виртуальными вызовами и может (в зависимости от кода) быть в состоянии вычленить неправильное (по крайней мере, если они безоговорочно возвращают константу).Однако, если вы не можете изменить семантику, опять же, этого не произойдет.Если это действительно важно, сделай это сам.В любом случае, проверьте, не является ли это узким местом вообще.

2 голосов
/ 22 июля 2011

Я не думаю, что компилятор может быть таким умным.Он не может знать, как реализован метод getRows().Что если он возвращает разные значения при каждом вызове?

Итак, суть.Вторая версия кода намного лучше.Не только с точки зрения производительности.Это более читабельно, и я считаю, что вы должны предпочесть этот метод при кодировании.

Единственный вызов метода, который я позволяю себе записать в цикл for, - это вызов length() метода массива.Массив не является "реальным" классом, и его метод length () не является реальным методом, поэтому его вызов не влияет на производительность.

1 голос
/ 23 июля 2011

javac не оптимизирует такие вещи.JVM делает.

Это зависит от того, что у вас в getRows() и getCols().Если они довольно просты, то почти наверняка они будут встроены в JVM;затем JVM может продолжить оптимизацию, вызвать их один раз и получить результат, если он может сделать вывод, что значения не меняются.

Например, следующий код

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

будет агрессивнооптимизированный JVM, никакая ручная оптимизация нами не может превзойти его.

0 голосов
/ 23 июля 2011

Компилятор почти не оптимизируется, однако компилятор Sun / Oracle достаточно умен, чтобы встроить до двух «виртуальных» методов. В зависимости от сложности получателей, это может не иметь значения.

Он должен быть достаточно умен, чтобы ваши петли были такими же, как

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 http://wikis.sun.com/display/HotSpotInternals/PrintAssembly

0 голосов
/ 22 июля 2011

Было бы довольно ужасно, если бы компилятор оценивал эти выражения только один раз, потому что у вас могут быть ситуации, когда вы хотите, чтобы они оценивались более одного раза.Но вы можете написать небольшой тест:

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--;
    }

}

Вывод:

0
1
2

Это показывает, что компилятор каждый раз вычисляет выражение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...