Цикл з нульовим часом виконання


74

Чи можна мати цикл, який має нульовий час виконання? Я думаю, що навіть порожній цикл повинен мати час виконання, оскільки з ним пов'язані накладні витрати.


37
цикли можуть бути розгорнуті та / або повністю усунені компілятором.
Елліотт Фріш,

4
Однією з основних завдань оптимізуючого компілятора є аналіз потоку даних. Обчислення, які дають дані, які нікуди не надходять, виключаються, включаючи цикли. Точна поведінка залежить від вашого компілятора.
Дітріх Епп

6
Закриття на основі занадто широкого запитання не має сенсу, у нас не так багато можливих відповідей на це питання, і це стосується загального випадку цілком добре.
Шафік Ягмор

3
Це питання не відповідає жодним критеріям занадто широкого. Як ми можемо емпірично бачити з відповідей, їх не надто багато, і в основному є одна точна відповідь, дві відповіді відрізняються лише кількістю деталей, які вони пропонують. Відповіді не надто довгі, і хоча їх можна було б розширити, їм доведеться пройти довгий шлях, перш ніж вони наблизяться до найдовших добрих відповідей на ідеальні тематичні питання, які там є.
Шафік Ягмор

2
@georg: але конвеєризація може зменшити вдвічі спостережуваний час обробки для певної інструкції, оскільки вона виконується разом з іншою в тому ж циклі процесора. Якщо "інша інструкція" - це ваш NOP, вам знадобиться лише оригінальний цикл інструкцій.
Jongware

Відповіді:


121

Так, за правилом немов компілятор зобов'язаний лише імітувати спостережувану поведінку коду, тому якщо у вас є цикл, який не має спостережуваної поведінки, його можна повністю оптимізувати і, отже, фактично матиме нульовий час виконання .

Приклади

Наприклад, такий код:

int main()
{
  int j = 0 ;
  for( int i = 0; i < 10000; ++i )
  {
    ++j ;
  }
}

компілюється з gcc 4.9використанням -O3прапора, в основному закінчується зведенням до наступного ( дивіться в прямому ефірі ):

main:
  xorl  %eax, %eax  #
  ret

Практично всі дозволені оптимізації підпадають під правило нібито , єдиним винятком, про який я знаю, є copy elison, якому дозволено впливати на спостережувану поведінку.

Деякі інші приклади включають усунення мертвого коду, яке може видалити код, який компілятор може довести, що ніколи не буде виконаний. Наприклад, навіть незважаючи на те, що наступний цикл дійсно містить побічний ефект, його можна оптимізувати, оскільки ми можемо довести, що він ніколи не буде виконаний ( див. Його в прямому ефірі ):

#include <stdio.h>

int main()
{
  int j = 0 ;
  if( false ) // The loop will never execute
  {
    for( int i = 0; i < 10000; ++i )
    {
      printf( "%d\n", j ) ;
      ++j ;
    }
  }
}

Цикл буде оптимізувати так само, як і попередній приклад. Більш цікавим прикладом може бути випадок, коли обчислення в циклі можна вивести у константу, тим самим уникаючи необхідності в циклі ( не впевнений, до якої категорії оптимізації це потрапляє ), наприклад:

int j = 0 ;
for( int i = 0; i < 10000; ++i )
{
  ++j ;
}
printf( "%d\n", j ) ;

можна оптимізувати ( переглянути його в прямому ефірі ):

movl    $10000, %esi    #,
movl    $.LC0, %edi #,
xorl    %eax, %eax  #
call    printf  #

Ми бачимо, що тут не задіяно жодної петлі.

Де як би Правило охоплюється стандартом

Як якби правило розглядається в проекті C99 стандарту розділі 5.1.2.3 Виконання програми , в якій мовиться:

В абстрактній машині всі вирази обчислюються відповідно до семантики. Фактична реалізація не повинна оцінювати частину виразу, якщо вона може зробити висновок про те, що його значення не використовується і що ніяких необхідних побічних ефектів не виробляється (включаючи будь-які, викликані викликом функції або доступом до нестабільного об'єкта).

Як якби правило також можна застосувати до C ++, gccбуде виробляти один і той же результат в режимі C ++ , а також. Проект стандарту C ++ охоплює це в розділі 1.9 Виконання програми :

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


Я б поклався на заклад, що більшість, якщо не всі мовні специфікації, мають правило як би . Зазвичай немає сенсу робити обчислення, якщо воно не використовується і не має побічних ефектів. Це лише витрачає час і сили.
Крейг С. Андерсон,

16
@CraigAnderson: Це правда , що переважна більшість часу, але є поодинокі випадки , коли ви дійсно хочете робити непотрібні обчислення, наприклад, в криптографічних операціях , де ви хочете , щоб всі шляхи коди взяти однакову кількість часу для того , щоб запобігти боку газорозподільної канальні атаки .
Адам Розенфілд,

Іншими словами : Для реальних циклів, які виконують фактичну, неминучу, недорогу роботу, відповідь: Ні . Для псевдоциклів, які роблять лише зайві речі: Так .
Lutz Prechelt

52

Так - якщо компілятор визначить, що цикл є мертвим кодом (ніколи не буде виконуватися), він не буде генерувати код для нього. Цей цикл матиме 0 часу виконання, хоча, строго кажучи, він не існує на рівні машинного коду.


12

Окрім оптимізації компілятора, деякі архітектури центрального процесора, зокрема DSP, мають нульовий цикл накладних витрат , завдяки чому цикл із фіксованою кількістю ітерацій ефективно оптимізується апаратним забезпеченням, див., Наприклад, http://www.dsprelated.com/showmessage/20681 /1.php


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.