From: Kaz Kylheku on 4 Nov 2009 20:25 On 2009-11-03, Captain Obvious <udodenko(a)users.sourceforge.net> wrote: > P> What's the advantage of using the MOP over a straight-up closure in > P> this instance? > > Might have better performance. If you use &rest and then apply > I guess most compilers will make a list, that is, you have more consing. > > (lambda (&rest args) > (let ((fn ....)) > (apply fn args))) It's a poor Lisp compiler which conses a list for code which only passes the &rest argument down to another function with apply. Pascal's code went right over your head there. The advantage of the MOP solution has nothing to do with this. And it has &rest/apply processing buried in it. What Pascal's code does is allow the user's function to be called several times without being compiled. It counts how many calls there are. When the calls exceed a threshold, then the object compiles the function. When the function is compiled, the object is able to replace its own closure with that compiled function. So subsequent calls go directly to that function, bypassing the threshold check. You can't do that with a lambda closure, because a lambda closure cannot replace future calls to itself with a different closure.
From: Kaz Kylheku on 4 Nov 2009 20:39 On 2009-11-03, Pascal Costanza <pc(a)p-cos.net> wrote: > If you want your code to be very sophisticated, you can actually also > implement your own version that uses interpreted code by default, counts > how often a particular runtime-generated function is called, and after a > certain threshold, compiles into a more efficient version (under the > assumption that it will continue to be called even more often). > > Here is a sketch [untested]: > > (defclass smart-function (funcallable-standard-object) () > (:metaclass funcallable-standard-class)) > > (defparameter *threshold* 10) > > (defun make-smart-function (body) > (let ((fun (make-instance 'smart-function))) > (set-funcallable-instance-function > fun > (let ((counter 0) (funfun (eval body))) > (lambda (&rest args) > (declare (dynamic-extent args) > (when (> (incf counter) *threshold*) > (unless (compiled-function-p funfun) > (setq funfun (compile nil body))) > (set-funcallable-instance-function fun funfun)) > (apply funfun args)))) > fun)) > > (defmacro smart-lambda ((&rest args) &body body) > `(make-smart-function `(lambda ,args ,@body))) > > > Yes, this requires the CLOS MOP. ;) Uh, you can do this with the existing standard funcallable objects. Namely, instances of this highly curious SYMBOL class, which have a standard slot given by the accessor SYMBOL-FUNCTION. :) ;; tested modifications to untested code ;; fixed unbalanced parentheses in declare ;; extra levels of unquote added to backquote ;; class replaced by symbol (defparameter *threshold* 10) (defun make-smart-function (body) (let ((fun (gensym "SMART-FUNCTION-"))) (setf (symbol-function fun) (let ((counter 0) (funfun (eval body))) (lambda (&rest args) (declare (dynamic-extent args)) (when (> (incf counter) *threshold*) (unless (compiled-function-p funfun) (setq funfun (compile nil body))) (setf (symbol-function fun) funfun)) (apply funfun args)))) fun)) (defmacro smart-lambda ((&rest args) &body body) `(make-smart-function `(lambda ,',args ,',@body))) Test: [4]> (defparameter x (smart-lambda (a b c) (list a b c))) X [5]> (symbol-function x) #<FUNCTION :LAMBDA (&REST ARGS) (DECLARE (DYNAMIC-EXTENT ARGS)) (WHEN (> (INCF COUNTER) *THRESHOLD*) (UNLESS (COMPILED-FUNCTION-P FUNFUN) (SETQ FUNFUN (COMPILE NIL BODY))) (SETF (SYMBOL-FUNCTION FUN) FUNFUN)) (APPLY FUNFUN ARGS)> [6]> (funcall x 1 2 3) (1 2 3) [7]> (funcall x 1 2 3) (1 2 3) [8]> (symbol-function x) #<FUNCTION :LAMBDA (&REST ARGS) (DECLARE (DYNAMIC-EXTENT ARGS)) (WHEN (> (INCF COUNTER) *THRESHOLD*) (UNLESS (COMPILED-FUNCTION-P FUNFUN) (SETQ FUNFUN (COMPILE NIL BODY))) (SETF (SYMBOL-FUNCTION FUN) FUNFUN)) (APPLY FUNFUN ARGS)> [9]> *threshold* 10 [10]> (symbol-function x) #<FUNCTION :LAMBDA (&REST ARGS) (DECLARE (DYNAMIC-EXTENT ARGS)) (WHEN (> (INCF COUNTER) *THRESHOLD*) (UNLESS (COMPILED-FUNCTION-P FUNFUN) (SETQ FUNFUN (COMPILE NIL BODY))) (SETF (SYMBOL-FUNCTION FUN) FUNFUN)) (APPLY FUNFUN ARGS)> [11]> (funcall x 1 2 3) (1 2 3) [12]> (funcall x 1 2 3) (1 2 3) [13]> (funcall x 1 2 3) (1 2 3) [14]> (funcall x 1 2 3) (1 2 3) [15]> (funcall x 1 2 3) (1 2 3) [16]> (funcall x 1 2 3) (1 2 3) [17]> (funcall x 1 2 3) (1 2 3) [18]> (funcall x 1 2 3) (1 2 3) [19]> (funcall x 1 2 3) (1 2 3) [20]> (funcall x 1 2 3) (1 2 3) [21]> (funcall x 1 2 3) (1 2 3) [22]> (symbol-function x) #<COMPILED-FUNCTION NIL> [23]> (funcall x 1 2 3) (1 2 3)
From: Pascal Costanza on 5 Nov 2009 03:56 Kaz Kylheku wrote: > On 2009-11-03, Pascal Costanza <pc(a)p-cos.net> wrote: >> If you want your code to be very sophisticated, you can actually also >> implement your own version that uses interpreted code by default, counts >> how often a particular runtime-generated function is called, and after a >> certain threshold, compiles into a more efficient version (under the >> assumption that it will continue to be called even more often). >> >> Here is a sketch [untested]: >> >> (defclass smart-function (funcallable-standard-object) () >> (:metaclass funcallable-standard-class)) >> >> (defparameter *threshold* 10) >> >> (defun make-smart-function (body) >> (let ((fun (make-instance 'smart-function))) >> (set-funcallable-instance-function >> fun >> (let ((counter 0) (funfun (eval body))) >> (lambda (&rest args) >> (declare (dynamic-extent args) >> (when (> (incf counter) *threshold*) >> (unless (compiled-function-p funfun) >> (setq funfun (compile nil body))) >> (set-funcallable-instance-function fun funfun)) >> (apply funfun args)))) >> fun)) >> >> (defmacro smart-lambda ((&rest args) &body body) >> `(make-smart-function `(lambda ,args ,@body))) >> >> >> Yes, this requires the CLOS MOP. ;) > > Uh, you can do this with the existing standard funcallable objects. Namely, > instances of this highly curious SYMBOL class, which have a standard > slot given by the accessor SYMBOL-FUNCTION. :) In principle yes, but unfortunately, there is a border case that doesn't quite work with symbols as functions: (defclass my-fun (funcallable-standard-object) () (:metaclass funcallable-standard-class)) (defmethod initialize-instance :after ((f my-fun) &key) (set-funcallable-instance-function f (lambda () 42))) * (setf (symbol-function 'foo) (make-instance 'my-fun)) #<MY-FUN {1002F44C09}> * (foo) 42 * (setf (symbol-function 'bar) 'foo) ; in: LAMBDA NIL ; (FUNCALL #'(SETF SYMBOL-FUNCTION) #:NEW671 'CLOSER-COMMON-LISP-USER::BAR) ; --> SB-C::%FUNCALL ; ==> ; (#<SB-C::GLOBAL-VAR ; :%SOURCE-NAME (SETF SYMBOL-FUNCTION) ; :TYPE #<SB-KERNEL:FUN-TYPE (FUNCTION #'SYMBOL #)> ; :WHERE-FROM :DECLARED ; :KIND :GLOBAL-FUNCTION {1002F4A281}> ; #:NEW671 'CLOSER-COMMON-LISP-USER::BAR) ; ; caught WARNING: ; Asserted type FUNCTION conflicts with derived type ; (VALUES (MEMBER FOO) &OPTIONAL). ; See also: ; The SBCL Manual, Node "Handling of Types" ; ; compilation unit finished ; caught 1 WARNING condition debugger invoked on a TYPE-ERROR: The value FOO is not of type FUNCTION. Some Common Lisp implementations accept this, some don't. Those that don't accept this are right not to do so, according to the HyperSpec. (I think this is an unnecessary restriction, symbols should be accepted as symbol-functions, but that's maybe just me... ;) 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: Captain Obvious on 6 Nov 2009 07:33 ??>> Might have better performance. If you use &rest and then apply ??>> I guess most compilers will make a list, that is, you have more ??>> consing. ??>> ??>> (lambda (&rest args) ??>> (let ((fn ....)) ??>> (apply fn args))) KK> It's a poor Lisp compiler which conses a list for code which only KK> passes the &rest argument down to another function with apply. Can you please show an example of a compiler which optimizes it? KK> You can't do that with a lambda closure, because a lambda closure KK> cannot replace future calls to itself with a different closure. That's why we have not one closure, but two closures, with first calling the second one -- then we can replace second closure with a compiled version when we would want to. There is overhead of indirection, but arguably it is very low. Here's code by Pascal: (defparameter *threshold* 10) (defun make-smart-function (body) (let ((counter 0) fun funfun) (setq funfun (eval body) fun (lambda (&rest args) (declare (dynamic-extent args)) (when (> (incf counter) *threshold*) (unless (compiled-function-p funfun) (setq funfun (compile nil body)) (setq fun funfun))) (apply funfun args))) (lambda (&rest args) (declare (dynamic-extent args)) (apply fun args)))) (defmacro smart-lambda ((&rest args) &body body) `(make-smart-function (lambda ,args ,@body))) Let's see does it go over your head or not.
First
|
Prev
|
Pages: 1 2 3 4 5 Prev: Article about lisp and array languages Next: lisp, java and evolution of types |