There is no secret in Faro's execution model. Recursive fragment
^ self * (self - 1) factorial
which happens in the second ifTrue: is compiled into the following sequence of bytecodes:
39 <70> self ; receiver of outer message * 40 <70> self ; receiver of inner message - 41 <76> pushConstant: 1 ; argument of self - 1 42 <B1> send: - ; subtract 43 <D0> send: factorial ; send factorial (nothing special here!) 44 <B8> send: * ; multiply 45 <7C> returnTop ; return
Note that on line 43, nothing special happens. The code simply sends factorial in the same way as if the selector were any other. In particular, we see that there are no special manipulations with the stack.
This does not mean that there can be no optimizations in the base native code. But this is another discussion. It is the execution model that matters to the programmer, since any optimization under bytecodes is designed to support this model at a conceptual level.
UPDATE
Interestingly, the non-recursive version
factorial2 | f | f := 1. 2 to: self do: [:i | f := f * i]. ^f
slightly slower than recursive (Pharo). The reason should be that the overhead associated with increasing i is slightly larger than the recursive sending mechanism.
Here are the expressions I tried:
[25000 factorial] timeToRun [25000 factorial2] timeToRun
Leandro caniglia
source share