On Thu, Jan 7, 2010 at 5:44 PM, Erik Huelsmann ehuels@gmail.com wrote:
Hi Alan,
On Thu, Jan 7, 2010 at 12:02 AM, Alan Ruttenberg alanruttenberg@gmail.com wrote:
The error turned out to be in PRECOMPILE-LAMBDA and PRECOMPILE-NAMED-LAMBDA. Committed the fix in r12340 in trunk. This will be in 0.18.
I think it would be a good idea to extend your inline-lambda-rewriter to be able to parse/rewrite all lambda lists, not only the ones with &rest arguments (did I see correctly that you restricted to that case?).
I did, for expediency. I don't know whether it will be worth doing it for all lambda lists, and full lambda list processing is complicated. I know that my code was using these forms - it might be worth seeing whether there are any uses of more complicated forms. At some point the additional cost of doing the lambda list processing relative to the extra function call might not be worth it.
But doesn't DESTRUCTURING-BIND already give us lambda-list processing for "free"? I mean, it's too lenient with processing the lambda list because it will allow all kinds of forms ordinary lambda lists won't allow, but it definitely parses them.
Don't mind doing a bit more on it if there are known cases that would benefit.
I don't know which software uses this a lot, but I think it's just very nice to support the general case, if there's support to be written anyway.
Here are a couple of tests manually doing the transform. The tradeoff for the simplistic version of this is that there will always be more consing.
(defun lvd-restarg () (declare (optimize (speed 3) (safety 0))) (let* ((a1 (random 100)) (b1 (1+ a1)) (c1 (1+ b1)) (d1 (1+ c1))) (flet ((bar (x y z) (setq s (+ x y (car z) (second z)) ))) (time (dotimes (i 200000) ((lambda (a b &rest c &aux) (bar a b c)) a1 b1 c1 d1))) ; force off current optimization (time (dotimes (i 200000) ((lambda (a b &rest c) (bar a b c)) a1 b1 c1 d1))) ; with current (time (dotimes (i 200000) (destructuring-bind (a b &rest c) (list a1 b1 c1 d1) (bar a b c))))) ; proposed ))
(defun lvd-no-restarg () (declare (optimize (speed 3) (safety 0))) (let* ((v1 (random 100)) (v2 (1+ v1)) (v3 (1+ v2)) (args (list v1 v2 :c v3)) (s nil)) (time (setq s (dotimes (i 200000) ((lambda (a &optional (b 2 b-supplied) &key (c 2)) (if b-supplied (+ a b c))) v1 v2 :c v3)))) (time (setq s (dotimes (i 200000) (destructuring-bind (a &optional (b 2 b-supplied) &key (c 2)) args (+ a b c))))) ))
CL-USER> (jvm::lvd-no-restarg) 0.734 seconds real time 6 cons cells 0.181 seconds real time 200074 cons cells NIL
(jvm::lvd-restarg) 1.486 seconds real time 400012 cons cells 0.663 seconds real time 400076 cons cells 0.653 seconds real time 800080 cons cells
The excess consing could be avoided if the expansion of the destructuring bind (or a transform of it) could be smarter.
(destructuring-bind (a b &rest c) (list a1 b1 c1 d1) (bar a b c)) -> (LET ((#:ARG-LIST-30182 (LIST A1 B1 C1 D1))) (LET* () (LET* ((A (CAR #:ARG-LIST-30182)) (B (CAR (CDR #:ARG-LIST-30182))) (C (CDR (CDR #:ARG-LIST-30182)))) (BAR A B C))))
The thing to notice is that (CAR #:ARG-LIST-30182) -> (car (list a1 b1 c1 d1)) -> a1, etc.
I think it would be definitely worth doing the transform to destructuring bind if the optimization to remove unnecessary consing was done.
If not, I'm not sure. What do you or others think - is the time savings worth the consing?
I will think about what's involved in doing that optimization.
-Alan