Есть ли разница в производительности между for () и while ()? - PullRequest
20 голосов

Ответы [ 16 ]

63 голосов
/ 11 мая 2009

Краткий ответ: нет, они точно такие же.

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

Просто для забавы вот два варианта, которые компилируются для меня в один и тот же ассемблерный код с использованием x86 gcc версии 4.3.3, поставляемой с Ubuntu. Вы можете проверить сборку, созданную в конечном двоичном файле, с помощью objdump в linux.

int main()
{
#if 1
    int i = 10;
    do { printf("%d\n", i); } while(--i);
#else
    int i = 10;
    for (; i; --i) printf("%d\n", i);
#endif
}

РЕДАКТИРОВАТЬ: Вот пример "апельсины с апельсинами" в то время как цикл, который также сводится к тому же:

    while(i) { printf("%d\n", i); --i; }
14 голосов
/ 11 мая 2009

Если ваш цикл for и while делает одно и то же, машинный код, сгенерированный компилятором, должен быть (почти) одинаковым.

Например, в некоторых тестах, которые я проводил несколько лет назад,

for (int i = 0; i < 10; i++)
{
...
}

и

int i = 0;
do
{
  ...
  i++;
}
while (i < 10);

сгенерирует точно такой же код или (и Нил указал в комментариях) с одним дополнительным jmp, что не даст достаточно большой разницы в производительности, чтобы беспокоиться

8 голосов
/ 11 мая 2009

Нет семантической разницы, не должно быть никакой скомпилированной разницы. Но это зависит от компилятора. Поэтому я попытался с g ++ 4.3.2, CC 5.5 и xlc6.

г ++, CC были идентичны, xlc НЕ БЫЛО

Разница в xlc была в начальной записи цикла.

extern int doit( int );
void loop1( ) {
  for ( int ii = 0; ii < 10; ii++ ) {
    doit( ii );
  }
}

void loop2() {
  int ii = 0; 
  while ( ii < 10 ) {
    doit( ii );
    ii++;
  }
}

ВЫХОД XLC

.loop2:                                 # 0x00000000 (H.10.NO_SYMBOL)
    mfspr   r0,LR
    stu     SP,-80(SP)
    st      r0,88(SP)
    cal     r3,0(r0)
    st      r3,64(SP)
    l       r3,64(SP)  ### DIFFERENCE ###
    cmpi    0,r3,10
    bc      BO_IF_NOT,CR0_LT,__L40
...
enter code here
.loop1:                                 # 0x0000006c (H.10.NO_SYMBOL+0x6c)
    mfspr   r0,LR
    stu     SP,-80(SP)
    st      r0,88(SP)
    cal     r3,0(r0)
    cmpi    0,r3,10    ### DIFFERENCE ###
    st      r3,64(SP)
    bc      BO_IF_NOT,CR0_LT,__La8
...
7 голосов
/ 11 мая 2009

Область действия переменной в тесте цикла while шире области действия переменных, объявленных в заголовке цикла for.

Поэтому, если есть побочные эффекты производительности в качестве побочного эффекта от того, чтобы поддерживать переменную дольше, то будут производственные последствия при выборе между циклом while и for (и не оборачиванием while в {}, чтобы уменьшить область его переменных).

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

6 голосов
/ 11 мая 2009

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

5 голосов
/ 11 мая 2009

Оба эквивалентны. Это вопрос семантики.

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

i = 1; do { ... i--; } while( i > 0 ); 

в отличие от

for(  i = 1; i > 0; --i )
{ ....
}  
4 голосов
/ 13 мая 2009

пишу компиляторы. Мы компилируем весь «структурированный» поток управления (if, while, for, switch, do ... while) в условные и безусловные ветви. Затем мы анализируем граф потока управления. Так как компилятор C в любом случае должен иметь дело с общими goto, проще всего свести все к инструкциям ветвления и условного ветвления, а затем убедиться, что он хорошо справляется с этим случаем. (Компилятор C должен хорошо выполнять не только рукописный код, но и автоматически сгенерированный код, который может содержать множество операторов goto.)

3 голосов
/ 11 мая 2009

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

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

for (; i < 1000; ++i);   
while (i < 1000) ++i;
3 голосов
/ 11 мая 2009

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

1 голос
/ 12 мая 2009

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

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

Последнее, где разница между for() и while() может, но, вероятно, не будет иметь значения.

P.S. Программисты, которых я знаю (которые все очень хороши, и я подозреваю, являются репрезентативной выборкой), в основном идут на это с другой стороны.

...