я просто был разговор, где были обсуждены следующие два peices кода C:Насколько «умным» является оптимизация Tail-Call GCC?
For-Loop:
#include <stdio.h>
#define n (196607)
int main() {
long loop;
int count=0;
for (loop=0;loop<n;loop++) {
count++;
}
printf("Result = %d\n",count);
return 0;
}
Рекурсивный:
#include <stdio.h>
#define n (196607)
long recursive(long loop) {
return (loop>0) ? recursive(loop-1)+1: 0;
}
int main() {
long result;
result = recursive(n);
printf("Result = %d\n",result);
return 0;
}
Увидев этот код, я видел recursive(loop-1)+1
и подумал: «ах, это не хвост, рекурсивный вызов», потому что он должен работать после завершения вызова recursive
; ему необходимо увеличить возвращаемое значение.
Конечно, без оптимизации рекурсивный код запускает переполнение стека, как и следовало ожидать.
с флагом -O2
, однако переполнение стека не встречается, что я подразумеваю, что стек используется повторно, а не толкает все больше и больше на стек - это tco.
GCC, очевидно, может обнаружить этот простой случай (+1 для возврата значения) и оптимизировать его, но как далеко он идет?
Каковы пределы того, что gcc может оптимизировать с помощью tco, когда рекурсивный вызов не является последней операцией, которую необходимо выполнить?
Приложение: Я написал полностью хвостовую рекурсивную версию кода return function();
. Обертывание выше в цикле с 9999999 итераций, я придумал следующие тайминги:
$ for f in *.exe; do time ./$f > results; done
+ for f in '*.exe'
+ ./forLoop.c.exe
real 0m3.650s
user 0m3.588s
sys 0m0.061s
+ for f in '*.exe'
+ ./recursive.c.exe
real 0m3.682s
user 0m3.588s
sys 0m0.093s
+ for f in '*.exe'
+ ./tail_recursive.c.exe
real 0m3.697s
user 0m3.588s
sys 0m0.077s
так (правда, простой и не очень строгий) тест показывает, что это действительно, как представляется, в том же порядке затраченное время.
Компилятор может просто вставить функцию вместо использования хвостовой рекурсии. Скомпилируйте программу с помощью флага '-S' и посмотрите, как выглядит код сборки. –
Я согласен с @squeamishossifrage. Не включайте оптимизацию, а затем просто предполагайте, что сделал компилятор. Вы можете быть удивлены, и это бесполезно. – unwind