Ну, я могу говорить только о C ++, потому что я новичок в Java. В C ++ компиляторы могут свободно игнорировать любые языковые требования, установленные стандартом, при условии, что наблюдаемое поведение равно как если бы компилятор фактически эмулировал все правила, которые помещаются Стандарт. Наблюдаемое поведение определяется как любое чтение и запись в изменчивые данные и вызовы библиотечных функций . Учтите это:
extern int x; // defined elsewhere
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
x += x + x + x + x + x;
}
return x;
Компилятору C ++ разрешено оптимизировать этот фрагмент кода и просто добавить правильное значение x, которое будет получено из этого цикла один раз, потому что код ведет себя как если бы цикл никогда не происходил, и нет изменчивых данных или библиотечных функций, которые могут вызвать побочные эффекты. Теперь рассмотрим изменчивые переменные:
extern volatile int x; // defined elsewhere
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
x += x + x + x + x + x;
}
return x;
Компилятору не разрешено выполнять ту же оптимизацию, поскольку он не может доказать, что побочные эффекты, вызванные записью в x
, не могут повлиять на наблюдаемое поведение программы. В конце концов, x можно установить в ячейку памяти, которую отслеживает какое-либо аппаратное устройство, которое будет запускаться при каждой записи.
Говоря о Java, я проверил ваш цикл, и бывает, что GNU Java Compiler (gcj
) занимает слишком много времени, чтобы завершить ваш цикл (он просто не завершился, и я его убил). Я включил флаги оптимизации (-O2), и это произошло, сразу же распечатав 0
:
[js@HOST2 java]$ gcj --main=Optimize -O2 Optimize.java
[js@HOST2 java]$ ./a.out
0
[js@HOST2 java]$
Может быть, это наблюдение может быть полезным в этой теме? Почему это происходит так быстро для gcj? Ну, одна из причин, безусловно, заключается в том, что gcj компилируется в машинный код, и поэтому у него нет возможности оптимизировать этот код на основе поведения кода во время выполнения. Он собирает все свои сильные стороны вместе и пытается оптимизировать как можно больше во время компиляции. Однако виртуальная машина может скомпилировать код Just in Time, как показано в выходных данных java для этого кода:
class Optimize {
private static int doIt() {
int x = 0;
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
x += x + x + x + x + x;
}
return x;
}
public static void main(String[] args) {
for(int i=0;i<5;i++) {
doIt();
}
}
}
Выход для java -XX:+PrintCompilation Optimize
:
1 java.lang.String::hashCode (60 bytes)
1% Optimize::doIt @ 4 (30 bytes)
2 Optimize::doIt (30 bytes)
Как видим, JIT компилирует функцию doIt 2 раза. Основываясь на наблюдении за первым выполнением, он компилирует его во второй раз. Но он имеет один и тот же размер, что и байт-код, два раза, предполагая, что цикл все еще на месте.
Как показывает другой программист, время выполнения для некоторых мертвых циклов даже увеличивается в некоторых случаях для впоследствии скомпилированного кода. Он сообщил об ошибке, которую можно прочитать здесь и по состоянию на 24 октября 2008 года.