Преимущества использования краткой оценки

StackOverflow https://stackoverflow.com/questions/89154

  •  01-07-2019
  •  | 
  •  

Вопрос

boolean a = false, b = true;
if ( a && b ) { ... };

В большинстве языков b не будет оценен, потому что a это ложь, поэтому a && b не может быть правдой.Мой вопрос: не будет ли короткое замыкание медленнее с точки зрения архитектуры?В конвейере вы просто останавливаетесь, ожидая получения результата a, чтобы определить, следует ли оценивать b или нет?Было бы лучше вместо этого использовать вложенные if?Это вообще помогает?

Кроме того, кто-нибудь знает, что обычно называют оценкой короткого замыкания?Этот вопрос возник после того, как я узнал, что мой друг-программист никогда не слышал о сокращенной оценке и заявил, что она не распространена, не встречается во многих языках и неэффективна в конвейере.Я не уверен насчет последнего, поэтому спрашиваю вас, ребята!

Хорошо, я думаю, что можно привести другой пример, чтобы объяснить, откуда мог родиться мой друг.Он считает, что, поскольку параллельно оценивается такое утверждение:

(a) if ( ( a != null ) && ( a.equals(b) ) ) { ... }

приведет к сбою системы, архитектура, которая не имеет короткого замыкания (и, следовательно, не допускает операторов, подобных приведенным выше), будет быстрее обрабатывать такие операторы:

(b) if ( ( a == 4 ) && ( b == 5 ) )

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

Я не знаю, правда это или нет.

Спасибо

Это было полезно?

Решение

Сокращенные логические выражения в точности эквивалентны некоторому набору вложенных if, поэтому они настолько эффективны, насколько это возможно.

Если b не имеет побочных эффектов, его все равно можно выполнять параллельно с a (для любого значения «параллельно», включая конвейерную обработку).

Если b имеет побочные эффекты, которые архитектура ЦП не может отменить при неудачном прогнозировании ветвления, тогда да, это может потребовать задержек, которых не было бы, если бы обе стороны всегда оценивались.Так что на это стоит обратить внимание, если вы когда-нибудь обнаружите, что операторы короткого замыкания создают узкое место в производительности вашего кода, но в противном случае не стоит беспокоиться.

Но короткое замыкание используется не только для потока управления, но и для экономии ненужной работы.Это распространено среди языков, которые я использовал, например идиома Perl:

open($filename) or die("couldn't open file");

идиома оболочки:

do_something || echo "that failed"

или идиома C/C++/Java/etc:

if ((obj != 0) && (obj->ready)) { do_something; } // not -> in Java of course.

Во всех этих случаях вам необходимо короткое замыкание, чтобы RHS оценивалась только в том случае, если LHS диктует, что это должно быть.В таких случаях нет смысла сравнивать производительность с альтернативным кодом, который ошибочен!

Другие советы

Вычисление короткого замыкания переводится в ветки на ассемблере таким же образом, как операторы if is (ветви — это, по сути, переход), что означает, что он не будет медленнее, чем операторы if.

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

Оценка короткого замыкания также является наиболее распространенным названием и в той или иной форме встречается в большинстве языков.

Честно говоря, я бы не беспокоился об этом.Тестирование логического значения действительно быстро.Короткое замыкание становится интересным/полезным только тогда, когда второе выражение имеет побочные эффекты:

if ( ConfirmAction() && DestroyAllData() )
   Reboot();

...или зависит от первого теста:

if ( myDodgyVar != null && myDodgyVar.IsActive() )
   DoSomethingWith(myDodgyVar);

Языки, поддерживающие короткое замыкание:

Ada, Eiffel, ALGOL 68, C1, C++, C#, Java, R, Erlang, Standard ML, Javascript, MATLAB, Lisp, Lua, Scheme, OCaml, Haskell, Pascal, Perl, Ruby, PHP, Python, Smalltalk, Visual Basic .СЕТЬ

Взято из Оценка короткого замыкания

VB.Net имеет другой синтаксис в зависимости от того, хотите ли вы, чтобы он был закорочен или нет.По причинам, связанным с наследием, поведением по умолчанию является отсутствие короткого замыкания.Синтаксис следующий

Без короткого замыкания

IF A And B THEN
    ...
END IF

Короткое замыкание

IF A AndAlso B THEN
    ...
END IF

Вы можете использовать Or/OrElse, если хотите замкнуть оператор OR.Что действительно приятно в таких ситуациях, как следующая

If MyObj IsNot Nothing AndAlso MyObj.Value < SomeValue Then
    ....
End If

Лично я понимаю, что короткое замыкание может ускорить процесс, но это определенно не очевидно при простом взгляде на код.Я мог видеть, как неопытный разработчик сбивается с толку таким поведением.Это даже похоже на то, что может произойти или нет, в зависимости от флагов компилятора для уровня оптимизации.Мне нравится, как VB подробно описывает, какое поведение вы на самом деле хотите достичь.

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

В любом случае, даже в простом непараллельном языке я не понимаю, насколько бы вложенные if были бы быстрее, поскольку выполнение блокировалось бы до тех пор, пока не будет вычислено первое условие.

Как может вложенное, если не глохнуть?На самом деле, если a и b являются переменными, а не выражениями с побочными эффектами, хороший компилятор может загрузить их параллельно.Нет никакой пользы от использования большего количества if, кроме увеличения количества строк.На самом деле, это было бы худшей ошибкой в ​​отношении компилятора.

Это называется оценкой короткого замыкания.

Полезное короткое замыкание, которое я использую, выглядит примерно так:

if (a != null && a.equals(somevalue)) {
    ... do something.
}

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

Хотя все мое мнение.

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

Пример в вопросе представляет собой довольно упрощенный пример, который на самом деле не дает большого выигрыша в производительности.Выигрыш в производительности достигается, когда выражения сложны для вычисления.

В качестве примера того, когда это было бы хорошо, представьте себе игровую программу, которая имеет что-то вроде:

if (someObject.isActive() && someOtherObject.isActive() && CollisionDetection.collides(someObject, someOtherObject) {
  doSomething();
}

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

Короткое замыкание или минимальная оценка — это всего лишь синтаксический сахар для вложенных if.Считать, что это неэффективно или вызывает остановки, — это случай преждевременной оптимизации.На этом этапе большинство компиляторов достаточно умны, чтобы правильно интерпретировать и оптимизировать эти операторы.Использование этих операторов может значительно уменьшить вложенность и, следовательно, улучшить читаемость кода, что должно быть вашей целью номер один.

Что касается эффективности короткого замыкания, то конвейеризация вряд ли окажет существенное влияние на производительность.В ситуациях, когда это может оказать влияние, ничто не мешает компилятору проверять несколько условий параллельно, если эти тесты не имеют побочных эффектов.Кроме того, современные процессоры имеют несколько механизмов, полезных для повышения производительности конвейера разветвленного кода.

Вложенные if будут иметь тот же эффект, что и короткое замыкание &&.

«Оценка короткого замыкания» — наиболее распространенное название этого явления, и ваш друг ошибается, говоря, что это редкость;это очень распространено.

Я ничего не знаю о конвейерной обработке, но сокращенное вычисление — общая черта многих языков (и это также название, под которым я его знаю).В C && и другие операторы определяют точка последовательности точно так же, как ;делает оператор, поэтому я не понимаю, почему укороченная оценка менее эффективна, чем простое использование нескольких операторов.

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

Один поток является последовательным, поэтому, если у вас есть два if, первое, конечно, будет оцениваться раньше второго, поэтому я не вижу в этом разницы.Я использую оператор условного И (который && называется afaik) гораздо чаще, чем вложенные if.Если я хочу проверить что-то, оценка чего может занять некоторое время, я сначала провожу более простой тест, а затем более сложный после условного и.

a = obj.somethingQuickToTest() && obj.somethingSlowToTest();

кажется, ничем не отличается от

a = false;
if(obj.somethingQuickToTest())
   a = obj.somethingSlowToTest();

В зависимости от контекста его также можно назвать «Охраняющим».

И я видел это практически на каждом языке, с которым работал, а их около дюжины.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top