Очки последовательности, условия и оптимизации - PullRequest
6 голосов
/ 25 января 2011

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

Моя коллега заявляет, что когда оптимизация включена, компилятор может изменить порядок некоторых инструкций. Так что:

function foo(int a, int b)
{
  if (a > 5)
  {
    if (b < 6)
    {
      // Do something
    }
  }
}

Может быть изменено на:

function foo(int a, int b)
{
  if (b < 6)
  {
    if (a > 5)
    {
      // Do something
    }
  }
}

Конечно, в этом случае не меняет общее поведение программы и не действительно важно.

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

Итак, дорогие пользователи SO, что это за истина?

Ответы [ 7 ]

12 голосов
/ 25 января 2011

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

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

Даже если у вас было:

if (a > 5 && b < 6)

компилятор может свободно переставить это на

if (b < 6 && a > 5)

, поскольку между ними нет заметной разницы (в данном конкретном случае, когда a и b являются значениями int). [Предполагается, что безопасно читать как a, так и b; если чтение одного из них может привести к некоторой ошибке (например, у одного есть значение прерывания), то компилятор будет более ограничен в том, какие оптимизации он может сделать.]

11 голосов
/ 25 января 2011

Поскольку нет наблюдаемой разницы между двумя фрагментами программы - при условии, что реализация - это та, которая не использует значения ловушек или что-то еще, что может заставить внутреннее сравнение делать что-то другое, чем просто оценка дляtrue или false - компилятор может оптимизировать один для другого по правилу «как будто».Если бы было какое-то заметное отличие или какой-то способ, которым соответствующая программа могла бы вести себя по-другому, компилятор был бы не соответствующим, если бы он изменил одну форму на другую.

Для C ++ см. 1.9 [intro.execution] /5.

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

[Это положениеиногда называется правилом «как если бы», потому что реализация может свободно игнорировать любое требование этого международного стандарта, если результат равен , как если бы требование было выполнено, насколько это может быть определеноиз наблюдаемого поведения программы.Например, фактическая реализация не должна оценивать часть выражения, если она может сделать вывод, что ее значение не используется и что не возникает никаких побочных эффектов, влияющих на наблюдаемое поведение программы.]

1 голос
/ 25 января 2011

Точки последовательности применимы только к абстрактной машине.

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

1 голос
/ 25 января 2011

Да, оператор if является точкой последовательности .

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

0 голосов
/ 26 января 2011

Много ответов программиста ПК =)

Компилятор может и, скорее всего, оптимизирует точки последовательности для скорости, если «b» передается функции в быстродействующем регистре, а «a» передается в стеке. Это довольно распространенный случай для многих компиляторов для 8-битных и 16-битных MCU: s.

Благодаря оптимизации не нужно сначала складывать «b», затем загружать «a» в регистр, затем оценивать «a», затем загружать «b» обратно в регистр, а затем оценивать «b». Весьма беспорядок, я бы предпочел, чтобы компилятор обрабатывал перестановку точек последовательности.

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

0 голосов
/ 25 января 2011

Правда состоит в том, что если a> 5 ложно чаще, чем b <6 ложно, или наоборот, то последовательность будет иметь очень незначительное значение, так как ей придется вычислять оба условия в большем количестве случаев. </p>

На самом деле, хотя это и так тривиально, в данном конкретном случае не стоит беспокоиться.

Есть случаи, когда это действительно имеет значение, т.е. когда вы фильтруете большой набор данных по нескольким критериям и должны решить, какой фильтр применять первым, особенно если только один из них - O (log N) или константа и последующие проверки являются линейными через то, что осталось.

0 голосов
/ 25 января 2011

Конец полного выражения (включая те, которые управляют логическими конструкциями, такими как if, while и т. Д.) Является точкой последовательности.Однако точка последовательности действительно только дает гарантию того, что побочные эффекты ранее оцененных операторов завершены.

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

...