Мне кажется, что было бы идеально работать для оптимизации хвостовой рекурсии как в C, так и в C ++, но при отладке я никогда не вижу стека фреймов, который указывает на эту оптимизацию. Это хорошо, потому что стек говорит мне, насколько глубока рекурсия. Тем не менее, оптимизация тоже была бы неплохой.
Есть ли в C ++ компиляторы эту оптимизацию? Зачем? Почему нет?
Как мне сказать компилятору сделать это?
- Для MSVC:
/O2
или/Ox
- Для GCC:
-O2
или-O3
Как насчет проверки, если компилятор сделал это в определенном случае?
- Для MSVC включите вывод PDB, чтобы иметь возможность отслеживать код, а затем проверить код
- Для GCC ..?
Я бы по-прежнему принимал предложения о том, как определить, оптимизирована ли определенная функция таким образом компилятором (хотя я нахожу это обнадеживающим, что Конрад говорит мне принять это)
Всегда можно проверить, делает ли это компилятор вообще, выполняя бесконечную рекурсию и проверяя, приводит ли это к бесконечному циклу или переполнению стека (я сделал это с GCC и выяснил, что этого -O2
достаточно), но я хочу быть возможность проверить определенную функцию, которая, как я знаю, в любом случае прекратится. Я хотел бы иметь простой способ проверить это :)
После некоторого тестирования я обнаружил, что деструкторы разрушают возможность этой оптимизации. Иногда может стоить изменить область видимости определенных переменных и временных переменных, чтобы убедиться, что они выходят за пределы области действия до начала оператора return.
Если какой-либо деструктор должен быть запущен после хвостового вызова, оптимизация хвостового вызова не может быть выполнена.
источник
gcc
имеет более узкую опцию-foptimize-sibling-calls
«оптимизировать родственные и хвостовые рекурсивные вызовы». Эта опция (поgcc(1)
страницам справочника версий 4.4, 4.7 и 4.8 , ориентированный на различные платформы) включена на уровне-O2
,-O3
,-Os
.gcc 4.3.2 полностью вставляет эту функцию (дрянная / тривиальная
atoi()
реализация) вmain()
. Уровень оптимизации есть-O1
. Я замечаю, что если я поиграюсь с этим (даже изменив его сstatic
наextern
, хвостовая рекурсия уходит довольно быстро, поэтому я не буду зависеть от нее в правильности программы.источник
extern
метод может быть встроен.-O1
нет нет встраивания и нет оптимизации хвостовой рекурсии . Вы должны использовать-O2
для этого (ну, в 4.2.x, который довольно древний, он все еще не будет встроен). Кстати, также стоит добавить, что gcc может оптимизировать рекурсию, даже если она не является строго хвостовой (как, например, факториал без аккумулятора).Помимо очевидного (компиляторы не выполняют такого рода оптимизацию, пока вы не попросите об этом), есть сложность в оптимизации хвостового вызова в C ++: деструкторы.
Учитывая что-то вроде:
Компилятор не может (в общем) хвостовой вызов оптимизировать это, потому что он должен вызвать деструктор
cls
после возвращения рекурсивного вызова.Иногда компилятор может видеть, что деструктор не имеет внешне видимых побочных эффектов (поэтому это может быть сделано рано), но часто это не может.
Особенно распространенной формой этого является то, где
Funky
на самом деле являетсяstd::vector
или подобным.источник
Большинство компиляторов не выполняют никакой оптимизации в отладочной сборке.
Если вы используете VC, попробуйте сборку релиза с включенной информацией о PDB - это позволит вам проследить через оптимизированное приложение, и вы должны надеяться увидеть то, что вам нужно. Тем не менее, обратите внимание, что отладка и отслеживание оптимизированной сборки повсюду вас перепрыгивают, и часто вы не можете напрямую проверять переменные, поскольку они только попадают в регистры или полностью оптимизируются. Это "интересный" опыт ...
источник
Как упоминает Грег, компиляторы не будут делать это в режиме отладки. Это нормально для отладочных сборок, которые работают медленнее, чем сборка prod, но они не должны вылетать чаще: и если вы зависите от оптимизации хвостового вызова, они могут сделать именно это. Из-за этого часто лучше переписать хвостовой вызов как обычный цикл. :-(
источник