Компиляторы C++ могут оптимизировать “если” операторы внутри “для” циклов?

@ eed3si9n: Я даже предположил, что «1» - магическое число. : -)

Принцип, связанный с магическими числами, состоит в том, что каждый факт, с которым связан ваш код, должен быть объявлен ровно один раз. Если вы используете магические числа в своем коде (например, пример длины пароля, который дал @marcio, вы можете легко закончить дублирование этого факта, и когда вы поймете об этом факте, у вас возникла проблема обслуживания.

25
задан 22 September 2009 в 21:25
поделиться

7 ответов

Пробовал с GCC и -O3:

void foo();
void bar();

int main()
{
    bool doesnt_change = true;
    for (int i = 0; i != 3; ++i) {
        if (doesnt_change) {
            foo();
        }
        else {
            bar();
        }
    }
}

Результат для основного:

_main:
pushl   %ebp
movl    %esp, %ebp
andl    $-16, %esp
call    ___main
call    __Z3foov
call    __Z3foov
call    __Z3foov
xorl    %eax, %eax
leave
ret

Таким образом, он оптимизирует выбор (и развертывает меньшие циклы).

Эта оптимизация не выполняется, если hasnt_change является глобальной.

17
ответ дан UncleBens 15 October 2019 в 16:39
поделиться

Как уже говорили многие: это зависит.

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

for (condition)
  do_it<flag>();
1
ответ дан sbi 15 October 2019 в 16:39
поделиться

Yes, if it is determined that flag doesn't change and can't be changed by do_something or do_something_else, it can be pulled outside the loop. I've heard of this called loop hoisting, but Wikipedia has an entry called "loop invariant code motion".

If flags is a local variable, the compiler should be able to do this optimization since it's guaranteed to have no effect on the behavior of the generated code.

If flags is a global variable, and you call functions inside your loop it might not perform the optimization - it may not be able to determine if those functions modify the global.

This can also be affected by the sort of optimization you do - optimizing for size would favor the non-hoisted version while optimizing for speed would probably favor the hoisted version.

In general, this isn't the sort of thing that you should worry about, unless profiling tells you that the function is a hotspot and you see that less than efficient code is actually being generated by going over the assembly the compiler outputs. Micro-optimizations like this you should always just leave to the compiler unless you absolutely have to.

19
ответ дан 28 November 2019 в 21:19
поделиться

Я уверен, что если компилятор может определить, что флаг останется постоянным, он может выполнить некоторую перестановку:

const bool flag = /* ... */;
for (..;..;..;)
{
    if (flag)
    {
        // ...
    }
    else
    {
        // ...
    }
}

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

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

1
ответ дан 28 November 2019 в 21:19
поделиться

В целом да. Но нет никакой гарантии, и места, где компилятор сделает это, вероятно, редки.

Что большинство компиляторов делают без проблем, так это вывод неизменяемых оценок из цикла, например, если ваше условие

if (a<b) ....

, когда a и b на них не влияет цикл, сравнение будет производиться один раз перед циклом.

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

В каких случаях было бы полезно разделение цикла?

a) очень плотный цикл, в котором 1 цикл требует значительных затрат
б) весь цикл с обеими частями не умещается в кэше кода

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

Без какого-либо тестирования я ожидаю а) единственного случая, когда такая оптимизация будет применена, потому что это не всегда лучший выбор:

В каких случаях разделение цикла было бы плохим?

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

[править]
Мне не удалось заставить VC9 разделить следующий цикл (один из немногих случаев, когда это может быть действительно полезно)

extern volatile int vflag = 0;

int foo(int count)
{
   int sum = 0;
   int flag = vflag;
   for(int i=0; i<count; ++i)
   {
      if (flag)
         sum += i;
      else
         sum -= i;
   }

   return sum;
}

[edit 2]
обратите внимание, что с int flag = true; вторая ветвь оптимизируется. (и нет, const здесь не имеет значения;))

Что это значит? Либо он этого не поддерживает, это не имеет значения, потому что мой анализ неверен ;-)

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

1
ответ дан 28 November 2019 в 21:19
поделиться

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

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

0
ответ дан 28 November 2019 в 21:19
поделиться

С осторожностью скажу, что так и будет. Может ли это гарантировать, что значение не будет изменено этим или другим потоком?

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

0
ответ дан 28 November 2019 в 21:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: