Компилятор оптимизирует выход из внутреннего цикла?

Код, который я имею, похож на это (все использование показанных сделанных):

bool done = false;
for(int i = 0; i < big; i++)
{
  ...
  for(int j = 0; j < wow; j++)
  {
    ...
    if(foo(i,j))
    {
       done = true;
       break;
    }
    ...
  }
  if(done) break;
  ...
}

будут любые компиляторы преобразовывать его в это:

for(int i = 0; i < big; i++)
{
  ...
  for(int j = 0; j < wow; j++)
  {
    ...
    if(foo(i,j))
      goto __done; // same as a labeled break if we had it
    ...
  }
  ...
}
__done:;

Примечание: В то время как я главным образом интересуюсь если if(done)break; обойден и удалил как мертвый код, я также интересуюсь если он и done удален в целом.

7
задан BCS 16 February 2011 в 23:07
поделиться

4 ответа

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

При этом это одна из немногих ситуаций, когда goto НЕ является плохой идеей . Смело используйте его, чтобы вырваться из внутренних петель.

Править

Просто попробовал следующее в VS2010, и он действительно оптимизирует внешнее условие:

bool done = false;
for(int i = 0; i < 10; i++)
{
    for(int j = 0; j < 10; j++)
    {
        if(i == 7 && j == 3)
        {
            done = true;
            break;
        }
    }
    if(done) break;
}
return 0;
14
ответ дан 6 December 2019 в 06:49
поделиться

Я не пытаюсь шутить, но ... какое это имеет значение? В общем, я считаю, что лучше всего позволить компиляторам выполнять свою работу, и эта задача - создавать «лучший» (обратите внимание, что «лучший» может варьироваться в зависимости от ваших потребностей) скомпилированный код с учетом вашего исходного кода. Любые соображения производительности в вашем коде следует определять с помощью профилировщика и хорошего рабочего знания алгоритмической сложности.

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

4
ответ дан 6 December 2019 в 06:49
поделиться

Я пробовал GCC 4.2.1 со следующим:

// Prevent optimizing out calls to foo and loop unrolling:
extern int big, wow;
bool foo(int,int);

void
bar()
{
    int done = false;
    for(int i = 0; i < big; i++)
    {
        for(int j = 0; j < wow; j++)
        {
            if(foo(i,j))
            {
                done = true;
                break;
            }
        }
        if(done)
            break;
    }
}

... и он сразу попадает в почтовый ящик с -O3 :

  33:   e8 fc ff ff ff          call   34 <bar()+0x34> ; call to foo*
  38:   84 c0                   test   %al,%al
  3a:   74 e5                   je     21 <bar()+0x21> ; next loop iteration
  3c:   83 c4 10                add    $0x10,%esp
  3f:   5b                      pop    %ebx
  40:   5e                      pop    %esi
  41:   5d                      pop    %ebp
  42:   c3                      ret

** * Это из несвязанного объектного файла, вызов 34 на самом деле является вызовом foo .

1
ответ дан 6 December 2019 в 06:49
поделиться

Компилятор GNU делает именно это, начиная с уровня оптимизации -O1 (я использую gcc 4.5.1 на x86_64)

call    _Z3fooii  // al = foo(i,j)
testb   %al, %al
jne .L14
...

, где .L14 - это метка, размещенная точно там, где вы поместили __done:

Возможно, лучше спросить: какой современный компилятор не выполняет эту оптимизацию?

7
ответ дан 6 December 2019 в 06:49
поделиться
Другие вопросы по тегам:

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