The accepted answer is incorrect .
There is a difference in the execution time of the iterative function and the recursive function, and the reason is the compiler optimization -foptimize-sibling-calls
added by -O3
.
First code:
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <stdint.h> double descgrad(double xo, double xnew, double eps, double precision){ if (fabs(xnew - xo) <= precision) { return xnew; } else { return descgrad(xnew, xnew - eps*2*xnew, eps, precision); } } double descgraditer(double xo, double xnew, double eps, double precision){ double Xo = xo; double Xn = xnew; while(fabs(Xn-Xo) > precision){ Xo = Xn; Xn = Xo - eps * 2*Xo; } return Xn; } int main() { time_t s1, e1, d1, s2, e2, d2; int i, iter = 10000000; double a1, a2; s1 = time(NULL); for( i = 0; i < iter; i++ ){ a1 = descgraditer(100,99,0.01,0.00001); } e1 = time(NULL); d1 = difftime( e1, s1 ); s2 = time(NULL); for( i = 0; i < iter; i++ ){ a2 = descgrad(100,99,0.01,0.00001); } e2 = time(NULL); d2 = difftime( e2, s2 ); printf( "time_iter: %ds, time_rec: %ds, ratio (iter/rec): %f\n", d1, d2, (double)d1 / d2 ) ; printf( "return values: %f, %f\n", a1, a2 ); }
The previous posts were correct, indicating that you need to repeat many times to average the interference in the environment. Given this, I discarded your differentiation function in favor of the time.h
difftime
function on the time_t
data, since in many iterations, nothing finer than the second is meaningless. In addition, I removed printfs in the test.
I also fixed a bug in the recursive implementation. The if-statement source code has been checked for fabs(xnew-xo) < precision
, which is incorrect (or at least different from the iterative implementation). Iterative loops, while fabs ()> precision, so a recursive function should not be recursively processed with precision <= . Adding iteration counts to both functions confirms that this fix makes the function logically equivalent.
Compile and run with -O3
:
$ gcc test.c -O3 -lrt -o dg $ ./dg time_iter: 34 s, time_rec: 0 s, ratio (iter/rec): inf return values: 0.000487, 0.000487
Compile and run without -O3
$ gcc test.c -lrt -o dg $ ./dg time_iter: 54 s, time_rec: 90 s, ratio (iter/rec): 0.600000 return values: 0.000487, 0.000487
Without optimization, iteration is performed BETTER than recursion.
In -O3
optimization, however, recursion starts ten millionth iterations in less than a second. The reason is that it adds -foptimize-sibling-calls
, which optimizes voice calls with sister and tail, which is exactly what your recursive function uses.
Of course, I ran all -O3
optimizations EXCEPT -foptimize-sibling-calls
:
$ gcc test.c -lrt -o dg -fcprop-registers -fdefer-pop -fdelayed-branch -fguess-branch-probability -fif-conversion2 -fif-conversion -fipa-pure-const -fipa-reference -fmerge-constants -ftree-ccp -ftree-ch -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-fre -ftree-sra -ftree-ter -funit-at-a-time -fthread-jumps -falign-functions -falign-jumps -falign-loops -falign-labels -fcaller-saves -fcrossjumping -fcse-follow-jumps -fcse-skip-blocks -fdelete-null-pointer-checks -fexpensive-optimizations -fgcse -fgcse-lm -fpeephole2 -fregmove -freorder-blocks -freorder-functions -frerun-cse-after-loop -fsched-interblock -fsched-spec -fschedule-insns -fschedule-insns2 -fstrict-aliasing -ftree-pre -ftree-vrp -finline-functions -funswitch-loops -fgcse-after-reload -ftree-vectorize $ ./dg time_iter: 55 s, time_rec: 89 s, ratio (iter/rec): 0.617978 return values: 0.000487, 0.000487
Recursion without tail call optimization works worse than iteration, just like compiling with NO optimization. Read about compiler optimization here .
EDIT:
As a validation, I updated my code, including return values. In addition, I set two static variables to 0 and incremented each on recursion and iteration to check the correct result:
int a = 0; int b = 0; double descgrad(double xo, double xnew, double eps, double precision){ if (fabs(xnew - xo) <= precision) { return xnew; } else { a++; return descgrad(xnew, xnew - eps*2*xnew, eps, precision); } } double descgraditer(double xo, double xnew, double eps, double precision){ double Xo = xo; double Xn = xnew; while(fabs(Xn-Xo) > precision){ b++; Xo = Xn; Xn = Xo - eps * 2*Xo; } return Xn; } int main() { time_t s1, e1, d1, s2, e2, d2; int i, iter = 10000000; double a1, a2; s1 = time(NULL); for( i = 0; i < iter; i++ ){ a1 = descgraditer(100,99,0.01,0.00001); } e1 = time(NULL); d1 = difftime( e1, s1 ); s2 = time(NULL); for( i = 0; i < iter; i++ ){ a2 = descgrad(100,99,0.01,0.00001); } e2 = time(NULL); d2 = difftime( e2, s2 ); printf( "time_iter: %ds, time_rec: %ds, ratio (iter/rec): %f\n", d1, d2, (double)d1 / d2 ) ; printf( "return values: %f, %f\n", a1, a2 ); printf( "number of recurs/iters: %d, %d\n", a, b ); }
Exit:
$ gcc optimization.c -O3 -lrt -o dg $ ./dg time_iter: 41 s, time_rec: 24 s, ratio (iter/rec): 1.708333 return values: 0.000487, 0.000487 number of recurs/iters: 1755032704, 1755032704
The answers are the same, and the repetition is the same.
It is also interesting to note that the static variable fetching / incrementing has a significant effect on tail call optimization, however recursion is still performed by iteration.