GCC оптимизация цикла while с выражением - PullRequest
0 голосов
/ 18 ноября 2011

У меня следующая ситуация:

while (node != NULL && has_all_except)
{
  ...
}

Если ни node, ни has_all_except не изменены в цикле, будет ли gcc оптимизировать цикл для вычисления выражения только один раз?

Я изучил статью в Википедии об оптимизации компилятора (http://en.wikipedia.org/wiki/Compiler_optimization), но не смог получить однозначный ответ. Мои смелости говорят, что она будет оптимизирована.

Ответы [ 2 ]

1 голос
/ 18 ноября 2011

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

0 голосов
/ 18 ноября 2011

Оператор && вычисляется лениво.

Если node != NULL равно false, has_all_except даже не будет учитываться. Это правило языка, а не оптимизация.

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

Вы можете реализовать этот вид «оптимизации» самостоятельно, используя goto для проверки этого (примечание: я не рекомендую использовать goto, но он переносимо «симулирует» эффект предлагаемой обсуждаемой оптимизации). Я думаю, что рассмотрение примера проясняет проблемы, о которых я говорил, например ::1014*

#include <stdlib.h>

int do_stuff(); // returns true/false if things were changed
int other_stuff(); // returns true/false if changed

int main() {
  int has_all_except = 1;
  void *node = &has_all_except;
  while (node != NULL && has_all_except) {
  int changed = 0; // flag to watch for changes
  nochanges: // Place to jump to to unconditionally
    changed |= do_stuff();
    changed |= other_stuff();
    if (!changed)
      goto nochanges; // Unconditional jump
  }
}

Проблема в том, что нам удалось ввести безусловный переход, , но сам безусловный переход применяется условно и , что условие не проще, чем первая часть && сама.

Это означает:

  1. Один дополнительный int в качестве "флага", чтобы увидеть, что что-то изменилось
  2. Сотрудничество с do_stuff() и other_stuff() - если что-либо не является кооперативным, то это невозможно (и маловероятно, что ваш компилятор сможет выяснить это для вас через модули trhanslation)
  3. Чем больше кода, тем меньше места в кэше вашего процессора
  4. Чем больше веток, тем больше возможностей для остановок конвейера, прогнозирования и худших прогнозов

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

...