Hi!
I have just re-written some code of mine using CLOS in order to
increase maintainability and readability. While I'm quite happy now
about how the code looks I'm not so happy about its performance. I
noticed a significant slow-down (execution time was increased by up to
60% percent) in time-critical parts. The funny thing is that I thought
these time-critical parts would be the same after the code re-write -
they're closures which don't use CLOS at all.
Well, so I thought. When comparing the new code with the old version I
noticed that closures created from CLOS methods seem to do more work
than closures created by normal functions although I expected them to
do exactly the same job. Here's a simple example to illustrate what
I'm talking about:
I compiled and loaded this file:
(eval-when (:compile-toplevel :execute :load-toplevel)
(proclaim '(optimize (speed 3)
(safety 0)
(space 0)
(debug 0)
(compilation-speed 0))))
(defun foo (x)
(lambda () x))
(defclass bar ()
((a :initarg :a :reader a)))
(defmethod baz ((bar bar))
(let ((x (a bar)))
(lambda () x)))
... and got these results:
* (disassemble (foo 1))
4811B0E6: ADD [EAX], AL
0E8: .ENTRY "DEFUN FOO"() ; FUNCTION
100: POP DWORD PTR [EBP-8]
;;; both functions have the same code from here
* (disassemble (baz (make-instance 'bar :a 1)))
4811B4CA: ADD [EAX], AL
4CC: ADD [EAX], AL
4CE: ADD [EAX], AL
4D0: .ENTRY "DEFMETHOD BAZ (BAR)"() ; FUNCTION
4E8: POP DWORD PTR [EBP-8]
;;; both functions have the same code from here
(In my actual code the difference between the closures is even bigger
- see below.) I have no idea about X86 assembler code and about how
closures are implemented but my na�ve guess was that in both cases the
created closure 'just' has to capture the value of X no matter where
it came from. A quick test with AllegroCl and Lispworks shows that
both of them actually seem to do what I expected - the disassembly is
the same for FOO and BAZ.
Is this a specific CMUCL/PCL issue? Is there a way to work around this
without going back to my non-CLOS version?
Thanks,
Edi.
PS: Here's an excerpt from by actual code.
1. The original closure created by a function:
4804EC8A: ADD [EAX], AL
C8C: ADD [EAX], AL
C8E: ADD [EAX], AL
C90: .ENTRY "DEFUN CREATE-STRING-MATCHER"() ; FUNCTION
;;; common part starts here
2. The 'same' closure created by a CLOS method:
4802E829: ADD [EAX], AL
2B: ADD [EAX], AL
2D: ADD [EAX], AL
2F: ADD [ESI+44], BH
32: ADD [EAX], EAX
34: DEC EAX
35: CALL #x30F4303C
3A: ADD CL, [EAX-25]
3D: RET
3E: ADD ECX, [EAX+11]
41: ADD [EAX], AL
43: SUB [EDI-49], BH
46: ADD [EAX], CH
;;; common part starts here