<p>Your numbers are too small to be meaningful. The difference between 166 ms and 196 ms is, in absolute terms, tiny. Who knows what other factors could be influencing that? VM warmup time, differences in memory allocation, or any host of other things could easily cause a discrepancy of that size. To be sure, you should make the numbers much bigger.</p>

<p>On my machine, running Racket v7.0, I increased the arguments from <code>40000000</code> to <code>1000000000</code> and ran the program. The results were 2.361 s for the internal definition case and 2.212 s for the external definition case. Given the sorts of factors listed above, that difference is too small to be meaningful.</p>

<p>Benchmarking is hard, and benchmarking languages that run on VMs and are JIT compiled is harder. Even if you account for warmup and GC, run lots of iterations and take the averages, and generally try to do things right, the results you get could still be nearly meaningless, as the 2017 OOPSLA paper <a href="https://arxiv.org/pdf/1602.00602.pdf" rel="nofollow noreferrer">Virtual Machine Warmup Blows Hot and Cold</a> explains:</p>

<blockquote>
  <p>Virtual Machines (VMs) with Just-In-Time (JIT) compilers are traditionally thought to execute programs in two phases: the initial warmup phase determines which parts of a program would most benefit from dynamic compilation, before JIT compiling those parts into machine code; subsequently the program is said to be at a steady state of peak performance. Measurement methodologies almost always discard data collected during the warmup phase such that reported measurements focus entirely on peak performance. We introduce a fully automated statistical approach, based on changepoint analysis, which allows us to determine if a program has reached a steady state and, if so, whether that represents peak performance or not. Using this, we show that <strong>even when run in the most controlled of circumstances, small, deterministic, widely studied microbenchmarks often fail to reach a steady state of peak performance on a variety of common VMs</strong>. Repeating our experiment on 3 different machines, we found that <strong>at most 43.5%</strong> of ⟨VM, benchmark⟩ pairs consistently reach a steady state of peak performance.</p>
</blockquote>

<p>Emphasis mine. Make sure you’re measuring what you think you’re measuring.</p>
Scheme: why is Internal Definition faster than External Definition?
<p>I tried running the program below</p>

<pre><code>(define (odd-internal x)
  (define (even x)
    (if (zero? x)
        #t
        (odd-internal (sub1 x))))
  (if (zero? x)
      #f
      (even (sub1 x))))

(define (odd-external x)
  (if (zero? x)
      #f
      (even (sub1 x))))

(define (even x)
  (if (zero? x)
      #t
      (odd-external (sub1 x))))

(begin (display "Using internal definition\n")
       (time (odd-internal 40000000)))

(begin (display "Using external definition\n")
       (time (odd-external 40000000)))
</code></pre>

<p>This is the result in Racket</p>

<pre><code>Using internal definition
cpu time: 166 real time: 165 gc time: 0
#f
Using external definition
cpu time: 196 real time: 196 gc time: 0
#f
</code></pre>

<p>There you can see using internal definition is quite a bit faster. I've tried running on Chez Scheme and the result is similar. Why is that?</p>