Again. this is very implementation specific!
I tested your code and since it does consume memory and do a lot of computations the order in which I tested the two interfered with the second result. I separated the two tests in each file and run each 40 times separately and looked at average running time for both. The differences were num: 1059.75 and iter: 1018.85. 4% difference on average, but might as well be 12% when picking two samples. I'd guess the running time of the same program might differ in more than 4% on average so the difference between these are irrelevant in one run.
You have an extra application in your code so to check how much impact an argument has I made this little test. The usage of the arguments in the base case is so that Scheme won't optimize them away:
(define (three-params x y z)
(if (zero? x)
(cons y z)
(three-params (- x 1) x x)))
(define (two-params x y)
(if (zero? x)
(cons x y)
(two-params (- x 1) x)))
(define (one-param x)
(if (zero? x)
(cons x x)
(one-param (- x 1))))
(define numtimes 100000000)
(time (one-param numtimes))
(time (two-params numtimes #f))
(time (three-params numtimes #f #f))
Comment out 2 of the three last lines and make 3 files. Compile them if you can. The do the average of several runs. I choose 50 and in Ikarus I get the following averages:
agvms diffms
one 402.4
two 417.82 15.42
three 433.14 15.32
two2 551.38 133.56 (with an extra addition compared to two)
Just looking at the 50 results I see that the times overlap between one, two, and three, but statistically it looks like the average argument cost 0,15ns. if
, zero?
, -
and cons
and gc
cost a lot more even if they are primitives. Neither extra applications nor extra arguments should be your concern. Sometimes a different implementation optimizes your code differently so changing from racket to ikarus, gambit or chicken might improve you code in production.