4

I'm writing an app with common lisp that uses opengl, and as the thing has grown I've realized I have a bit of a choice to make. I have a bunch of different classes which all need code to render them quickly and often, so I'm considering the following code structures for doing this:

(defgeneric render (x))
(defmethod render ((x vanilla-foo))
   (generic-foo-stuff)
   (vanilla-specific-stuff)
   (more-generic-foo-stuff))
(defmethod render ((x chocolate-foo))
   (generic-foo-stuff)
   (chocolate-specific-stuff)
   (more-generic-foo-stuff))

and so on, lots of these methods. The other alternative is to use a typecase statement:

(defun render (any-old-foo)
   (generic-foo-stuff)
   (typecase
      (vanilla-foo 
         (vanilla-specific-stuff))
      (chocolate-foo
         (chocolate-specific-stuff))
      ;;and so on, lots of cases here
    )
    (more-generic-foo-stuff))

I suppose these are both ugly in their own way, but my assumption is that lisp will be using some kind of hash table O(1) lookup under the hood that maps the argument type passed into the generic function to the location of the needed method, whereas the second method will require O(n) type comparisons to work its way through the typecase statement. On the other hand, depending how slow or fast the hashing might be, I might need thousands of foo flavors before the first method is really faster.

Is there a good performance reason I should like one over the other? I'm also open to other suggestions, or references to some kind of document that make it clearer to me what is going on under the hood in each case. I'm also curious about other instances of this sort of conflict you may have encountered.

Thanks!

  • You asked for references: see [Efficient Method Dispatch in PCL](http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-Andreas-PCL/for-web.pdf) and [Fast generic dispatch for Common Lisp](http://metamodular.com/generic-dispatch.pdf) – coredump Jun 03 '15 at 09:28

2 Answers2

6

At least you can make the code using generic functions less ugly by factoring out the generic stuff to :before and :after methods:

(defgeneric render (x))

(defmethod render :before (x) ; Do what always has to be done first
  (generic-foo-stuff))

(defmethod render :after (x) ;  Do what always has to be done last
  (more-generic-foo-stuff))

(defmethod render ((x vanilla-foo)) ; Do only vanilla-specific things here
   (vanilla-specific-stuff))

(defmethod render ((x chocolate-foo)) 
   (chocolate-specific-stuff))
Terje D.
  • 6,250
  • 1
  • 22
  • 30
5

One would use generic functions if one

  • has multiple implementations for various argument combinations
  • wants to make the function extensible
  • wants to assemble code by reusing pieces (methods)

Then we might as well rewrite your example using :before and :aftermethods.

If the code should be as fast and static as possible, then generic functions are not such a good choice.

If you need to plan ahead for performance, then you best make some experiments and measure the timing.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346