Почему Java не оптимизирует | = назначения? - PullRequest
0 голосов
/ 03 октября 2018

t*() всегда возвращает true для этого примера, где f*() всегда возвращает false.

Допустим, у нас есть следующее выражение

if ( f1() || t1() || f2() || t2() ){
    // do stuff        
}

Если это так,JVM оптимизирует выполнение и выполняет только f1() и t1(), потому что она «понимает», что независимо от того, что дают f2() и t2(), требование для ввода оператора if выполняется и поэтому дальнейшие вычисления не требуются.

Я работаю над кодом, в котором я написал что-то вроде этого:

boolean b = false;
b |= f1(); // A
b |= t1(); // B
b |= f2(); // C
b |= t2(); // D

Один из моих коллег увидел это и сказал, что он не уверен, но возможно, что Javaоптимизирует операторы C и D, потому что b всегда true от оператора B и далее, и это может привести к некоторым проблемам.

Я провел некоторые тесты, кажется, что все они работают правильновыполнено (это желаемое поведение), но я все еще задаюсь вопросом почему это не оптимизируется? Я подумал, что он может быть прав, и JVM понимает это однажды b верно, нет |= операция над ним изменит свое значение.

Ответы [ 3 ]

0 голосов
/ 03 октября 2018

Вызовы не оптимизируются, потому что JLS §15.26.2.Сложные операторы присваивания требуют вычисления выражения правой части.

Если выражение левого операнда не является выражением доступа к массиву, то:

  • Во-первых, левый операнд вычисляется для создания переменной.Если эта оценка завершается преждевременно, то выражение присваивания завершается преждевременно по той же причине;правый операнд не оценивается, и присвоение не происходит.
  • В противном случае значение левого операнда сохраняется, а затем правый операнд оценивается.

  • ...

Исторически сложилось, что короткое замыкание условно (&&,||), но не побитовые (&, |) операторы возвращаются как минимум к C (но, возможно, стоит отметить, что C не имел явного логического типа до 1999 года).

0 голосов
/ 03 октября 2018

Мне все еще интересно, почему это не оптимизируется?

Потому что это будет нарушением JLS.

Оператор

b |= f1();

эквивалентно

b = (boolean)(b | f1());

. Выше JLS требует , что b | f1() оценивается следующим образом:

  1. Получитезначение b.
  2. Вызов f1() и захват результирующего значения
  3. Применить оператор | к двум значениям.

JLS не не позволяет компилятору пропускать вызов f1(), если b равно true 1 .

Если вы хотите эту семантику (короткое замыкание),вам нужно использовать b = b || f1(); и так далее.(Как вы отметили: b ||= f1() является синтаксической ошибкой.)


1 - На самом деле, в ситуации, когда было невозможно наблюдать (в однопоточной программе), чтоf1() вызов произошел или не произошел, теоретически оптимизация была бы допустимой.Но вы сможете обнаружить оптимизацию, только внимательно изучив собственный код , генерируемый JIT-компилятором.Это может произойти только в том случае, если вызов не имеет побочных эффектов.

0 голосов
/ 03 октября 2018

Это больше о разнице между логическими и побитовыми операторами.

Составной оператор |= является побитовым оператором, что означает, что оба члена оцениваются.

Вы можете легко отладить это, установив тест, в котором b присваивается литерал true, за которым следует присвоение |= методу, который возвращает либо значение boolean, и имеет точку останова.,

Точка останова всегда срабатывает.

С другой стороны, «быстрая» оптимизация доступна только для логических операторов: || и &&.

Примечание: некоторые спецификации составного назначения здесь , но я не смог найти соответствующую часть в назначении |=.

...