Search code examples
performancelispcommon-lispsbclfactorial

Lisp: Measuring performance of functions


So far, while testing the speeds of different approaches to writing the same function, I've been using the time function, and generally it gives me a good indication of the relative speeds of different functions (since they typically differ by around 100k cycles).

While attempting to find the fastest approach for a factorial function, however, time has been lacking. Not only do these methods seem to differ by only 10k-30k cycles, but their overall time vary by around half that amount too (which is expected, I guess).

The three factorial functions:

(defun factorial-recusion (n)   ; 1st      
  (if (equal n 1) 
      1
      (* n (factorial (- n 1)))))

(defun factorial-loop (n)   ; 2nd
  (let ((solution 1))
    (loop for x from n downto 2
       do (setf solution (* solution x))
       finally (return solution))))

(defun factorial-do (n)     ; 3rd
  (let ((solution 1))
    (do ((x n (1- x)))
    ((< x 2) (return solution))
      (setf solution (* solution x)))))

So, I guess I have two questions:

1.) Which method of factorial should be fastest (if any actually is), and why?

2.) If I were to find out for myself the faster function through a general method, what's the best way to do so (for some reason I think LOC is a bad indicator of speed)? Perhaps there is a way to view the disassembly of Lisp bytecode? Or maybe there's a better, more rigorous way?

I'm currently running Linux 3.2.0-26, SBCL, on Ubuntu 12.04 x86-64.


Solution

  • SBCL does not compile to 'bytecode'. It compiles to native machine code.

    You can disassemble a Lisp function by using DISASSEMBLE.

    Speed in your factorial function is dominated by bignum arithmetic multiplication once the numbers get into the bignum range.

    To make it clearer: which iteration construct you use, DO or LOOP, does not matter much. Most of the time is spent multiplying bignums. The recursive version also does not make much of a difference.

    That's a typical benchmark problem: many simple numeric benchmarks are dominated by runtimes of a few arithmetic operations (like multiplication of bignums). They will not measure much of the different in speed of general language operations (like the various types of control flow).

    A slow Lisp with a fast bignum library will likely be faster than an optimizing Lisp compiler with Bignum code written in Lisp.