From: ··········@gmail.com
Subject: Inlining generic functions
Date: 
Message-ID: <1159913733.649342.73270@m7g2000cwm.googlegroups.com>
Suppose I have (defmethod test-func ((x fixnum)) (+ x 5)) used in the
context of (lambda () (test-func 5)  No matter what optimization
settings I use, SBCL doesn't inline the function call, and proclaiming
test-func to be inline doesn't accomplish anything either.

One way I can get the desired behavior is to do
(defun test-func (x)
  (typecase x
     (fixnum (+ x 5))
     .....  )))

which, of course, requires either enumerating all the cases beforehand
or making test-func macro-generated and leads to warnings about
unreachable code.

Other ideas for macrology require that I gain access to the inferred
type, and I am not sure if there is a portable way of doing it.
However, in any case, coding my own compile-time polymorphism feels
stupid, and I do want to take advantage of runtime dispatch as well.
However, in some inner loops, where the types are fully declared, I
really want to avoid dispatch overhead.

Does anyone have pointers on how to proceed, hopefully w/o writing my
own generic function system?

Thanks,
Alex

From: Rahul Jain
Subject: Re: Inlining generic functions
Date: 
Message-ID: <8764f110vx.fsf@nyct.net>
··········@gmail.com writes:

> Other ideas for macrology require that I gain access to the inferred
> type, and I am not sure if there is a portable way of doing it.
> However, in any case, coding my own compile-time polymorphism feels
> stupid, and I do want to take advantage of runtime dispatch as well.
> However, in some inner loops, where the types are fully declared, I
> really want to avoid dispatch overhead.
>
> Does anyone have pointers on how to proceed, hopefully w/o writing my
> own generic function system?

What would you like to happen when another method is defined that would
also show up in the computed effective method? What if the method for
that specific signature is redefined? CLOS requires that these changes
have a visible effect regardless of optimization settings. You should be
able to add some declarations that SBCL's or CMUCL's compiler will pick
up to inline a call based on type inference, but that doesn't help if
you're not using either of them.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Pascal Costanza
Subject: Re: Inlining generic functions
Date: 
Message-ID: <4oh0kuFdbh12U1@individual.net>
Rahul Jain wrote:
> ··········@gmail.com writes:
> 
>> Other ideas for macrology require that I gain access to the inferred
>> type, and I am not sure if there is a portable way of doing it.
>> However, in any case, coding my own compile-time polymorphism feels
>> stupid, and I do want to take advantage of runtime dispatch as well.
>> However, in some inner loops, where the types are fully declared, I
>> really want to avoid dispatch overhead.
>>
>> Does anyone have pointers on how to proceed, hopefully w/o writing my
>> own generic function system?
> 
> What would you like to happen when another method is defined that would
> also show up in the computed effective method? What if the method for
> that specific signature is redefined? CLOS requires that these changes
> have a visible effect regardless of optimization settings. You should be
> able to add some declarations that SBCL's or CMUCL's compiler will pick
> up to inline a call based on type inference, but that doesn't help if
> you're not using either of them.

In principle, it should be possible to recompile call sites where a 
generic function is inlined whenever its set of methods changes. There 
is a paper that describes such an approach at 
http://people.csail.mit.edu/jrb/Projects/partial-dispatch.htm


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Rahul Jain
Subject: Re: Inlining generic functions
Date: 
Message-ID: <87zmcboqqx.fsf@nyct.net>
Pascal Costanza <··@p-cos.net> writes:

> In principle, it should be possible to recompile call sites where a
> generic function is inlined whenever its set of methods changes. There
> is a paper that describes such an approach at
> http://people.csail.mit.edu/jrb/Projects/partial-dispatch.htm

Yes, this can be done, but then dynamic redefinition becomes
prohibitively expensive in a non-trivial system. What is really needed
is a way of indicating that a class and a generic function are "sealed",
which means future compilations of calls to that g-f where all
parameters can be inferred to be of a sealed class (or a class with a
sealed class anywhere in its c-p-l) can have the effective method
inlined, wherein the individual methods would be inlined, in turn.

This way it is the programmer's responsibility to make sure that
appropriate recompilations occur (you would only do this for delivery of
applications where you don't have lots of classes being dynamically
generated, anyway).

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Pascal Costanza
Subject: Re: Inlining generic functions
Date: 
Message-ID: <4okjqsFep3bfU1@individual.net>
Rahul Jain wrote:
> Pascal Costanza <··@p-cos.net> writes:
> 
>> In principle, it should be possible to recompile call sites where a
>> generic function is inlined whenever its set of methods changes. There
>> is a paper that describes such an approach at
>> http://people.csail.mit.edu/jrb/Projects/partial-dispatch.htm
> 
> Yes, this can be done, but then dynamic redefinition becomes
> prohibitively expensive in a non-trivial system.

It doesn't have to, as has been shown in implementations of virtual 
machines for Self, Smalltalk / Strongtalk, and Java. The key trick is 
not to compile code in the first place but to interpret bytecodes and 
only compile "hot spots" on demand. I guess something similar could be 
done based on minimally compiled Common Lisp, but this would require a 
major implementation effort (with the possible outcome that it actually 
proves me wrong).

> What is really needed
> is a way of indicating that a class and a generic function are "sealed",
> which means future compilations of calls to that g-f where all
> parameters can be inferred to be of a sealed class (or a class with a
> sealed class anywhere in its c-p-l) can have the effective method
> inlined, wherein the individual methods would be inlined, in turn.
> 
> This way it is the programmer's responsibility to make sure that
> appropriate recompilations occur (you would only do this for delivery of
> applications where you don't have lots of classes being dynamically
> generated, anyway).

Yes, that's probably the next best thing to do.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: ··········@gmail.com
Subject: Re: Inlining generic functions
Date: 
Message-ID: <1160086106.676694.251040@k70g2000cwa.googlegroups.com>
Pascal Costanza wrote:
> Rahul Jain wrote:
> > Pascal Costanza <··@p-cos.net> writes:
> >
> >> In principle, it should be possible to recompile call sites where a
> >> generic function is inlined whenever its set of methods changes. There
> >> is a paper that describes such an approach at
> >> http://people.csail.mit.edu/jrb/Projects/partial-dispatch.htm
> >
> > Yes, this can be done, but then dynamic redefinition becomes
> > prohibitively expensive in a non-trivial system.
>
> It doesn't have to, as has been shown in implementations of virtual
> machines for Self, Smalltalk / Strongtalk, and Java. The key trick is
> not to compile code in the first place but to interpret bytecodes and
> only compile "hot spots" on demand. I guess something similar could be
> done based on minimally compiled Common Lisp, but this would require a
> major implementation effort (with the possible outcome that it actually
> proves me wrong).
>
> > What is really needed
> > is a way of indicating that a class and a generic function are "sealed",
> > which means future compilations of calls to that g-f where all
> > parameters can be inferred to be of a sealed class (or a class with a
> > sealed class anywhere in its c-p-l) can have the effective method
> > inlined, wherein the individual methods would be inlined, in turn.
> >
> > This way it is the programmer's responsibility to make sure that
> > appropriate recompilations occur (you would only do this for delivery of
> > applications where you don't have lots of classes being dynamically
> > generated, anyway).
>
> Yes, that's probably the next best thing to do.
>
>
> Pascal

I decided to write a system to do what I wanted using compiler macros.
As far as I know, it works, but I really don't like how the code looks.
 Does anyone have pointers on a more elegant way of doing this?

The way the code works is:

(my-defstruct test-struct
   (slot1 0)
   (slot2 0))

(my-defstruct test-struct1
  (slot1 10)
  (slot 2 5))

(slot1 (make-test-struct1)) => 10
(disassemble
   (lambda () (declare (optimize (speed 3) (safety 0)))
       (slot1 (make-test-struct)))

does not include a jump for the access.

One thing I noticed is that in order for SBCL to inline the call, I
have to refer to the function by name; storing actual functions instead
of the names in the hash table and eliminating the  (function . )
doesn't seem to work (well, it works, but no inlining).

That said, does anyone have feedback on now todo this better?


;; Store the function map.  Each key is the name of the slot and
;; the value is an a-list of (struct-type accessor-name)
(defvar function-map (make-hash-table))


(defun record-function-type (struct-name slot-name)
  "Record that for the structure struct-name, there exists a
slot slot-name and thus, the accessor function should handle it.
This simply inserts the proper association into function-map"
  (let ((new-function
	 (find-symbol
	  (concatenate 'string (symbol-name struct-name) "-" (symbol-name
slot-name)))))
    (cond ((assoc struct-name (gethash slot-name function-map))
	   (setf (cdr (assoc struct-name (gethash slot-name function-map)))
new-function))
	  (t (setf (gethash slot-name function-map)
		   (cons (cons struct-name new-function)
			 (gethash slot-name function-map)))))))


(defmacro def-accessor-function (slot)
  "Creates an accessor function and the corresponding compiler-macro
for
a particular slot, containing all the types indexed by slot in the
function-map"
  (let ((struct (gensym)))
    `(progn
      (defun ,slot (,struct)
	(etypecase ,struct
	  ,@(loop for (type . func) in (gethash slot function-map)
		  collect `(,type (funcall (function,func) ,struct)))))
      (define-compiler-macro ,slot (,struct)
	`(let ((str ,,struct))
	  (etypecase str
	    ,@(loop for (type . func) in (gethash ',slot function-map)
		    collect `(,type (funcall (function ,func) str)))))))))))


(defmacro my-defstruct (name-and-options &rest slot-descriptions)
  "An equivalent of defstruct, which in addition to doing everything
that
defstruct does, also generic accessors for each slot.  Thus,
(my-defstruct test (slot1 5)) will also create a function slot1 which
when called with an instance of this structure, will return its slot1"
  (let ((struct-name name-and-options))
    `(progn
      (defstruct ,struct-name ,@slot-descriptions)
      ,@(loop for (slot-name . rest) in slot-descriptions
	      collect `(record-function-type ',struct-name ',slot-name)
	      collect `(def-accessor-function ,slot-name)
	      ;collect (print (macroexpand-1 `(def-accessor-function
,slot-name)))
	      )))))


Thanks,
Alex
From: Rahul Jain
Subject: Re: Inlining generic functions
Date: 
Message-ID: <87lknulc7q.fsf@nyct.net>
Pascal Costanza <··@p-cos.net> writes:

> Rahul Jain wrote:
>> Pascal Costanza <··@p-cos.net> writes:
>>
>>> In principle, it should be possible to recompile call sites where a
>>> generic function is inlined whenever its set of methods changes. There
>>> is a paper that describes such an approach at
>>> http://people.csail.mit.edu/jrb/Projects/partial-dispatch.htm
>>
>> Yes, this can be done, but then dynamic redefinition becomes
>> prohibitively expensive in a non-trivial system.
>
> It doesn't have to, as has been shown in implementations of virtual
> machines for Self, Smalltalk / Strongtalk, and Java. The key trick is
> not to compile code in the first place but to interpret bytecodes and
> only compile "hot spots" on demand. I guess something similar could be
> done based on minimally compiled Common Lisp, but this would require a
> major implementation effort (with the possible outcome that it actually
> proves me wrong).

You're right, to some extent. You'd only recompile the code that was
declared with (optimize speed) because that's the only case where you'd
actually inline the effective-method and the methods it calls. (Assuming
the way _I_ would want it to work.)

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist