Subject: Re: do and cmucl
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 28 Aug 2007 03:37:45 -0500
Newsgroups: comp.lang.lisp
Message-ID: <uK6dnf0I_eVEQ07bnZ2dnUVZ_q-jnZ2d@speakeasy.net>
Andreas Klein  <klein@cage.ugent.be> wrote:
+---------------
| Hello I found a strange behaviour of cmucl with nested dos.
+---------------

Strange to you, maybe, expected by CMUCL users.

+---------------
| Here is my simple example
| 
| (do ((i 1 (+ 1 i)))
|     ((= i 1000) nil)
|   (do ((j 1 (+ 1 j)))
|       ((= j 1000) nil)
|     (+ i j)))
| 
| And here is what cmucl says
| 
| ; [GC threshold exceeded with 12,013,104 bytes in use.  Commencing GC.]
| ; [GC completed with 2,267,200 bytes retained and 9,745,904 bytes
| freed.]
+---------------

What, you don't want to see GC messages by default? ;-}  ;-}
O.k., just put a (SETF *GC-VERBOSE* NIL) in your
"~/.cmucl-init" file. Personally, I prefer to see
them while developing [and then turn them off in
production, if naive users might see them], but YMMV.

+---------------
| It seams that it wastes a lot of memory.
+---------------

*Conses* a lot, perhaps, but it also recovers *all* of it, too.
On my system, the above runs in less than 4 seconds of elapsed wall
clock time and only GCs 9 times, never using more than ~13.5 MB max
and freeing 12 MB with each GC. Not a problem.

+---------------
| All other common lisp implementations I tried (clisp, gcl, sbcl)
| works fine with that code.
+---------------

Works fine in CMUCL, too, just maybe noisier than you prefer.
[But you can fix that.]

+---------------
| Does anyone knows what happens with cmucl...
+---------------

CMUCL defaults to *GC-VERBOSE* being T.

+---------------
| and how I can correct it?
+---------------

Two ways:

1. Turn off GC reporting: (SETF *GC-VERBOSE* NIL)

2. Compile the code. The compiler will keep all the intermediate
   results in registers, and since none is bigger than a fixnum,
   no consing of intermediates happens. Note that compilation is
   automatic if you call TIME (*and* you get see how fast it goes):

      > (time
	  (do ((i 1 (+ 1 i)))
	      ((= i 1000) nil)
	    (do ((j 1 (+ 1 j)))
		((= j 1000) nil)
	      (+ i j))))
      ; Compiling LAMBDA NIL: 
      ; Compiling Top-Level Form: 

      ; Evaluation took:
      ;   0.01f0 seconds of real time
      ;   0.009581f0 seconds of user run time
      ;   0.0f0 seconds of system run time
      ;   22,482,898 CPU cycles
      ;   0 page faults and
      ;   0 bytes consed.
      ; 
      NIL
      > 

In general, if a Lisp implementation *has* a compiler [as CMUCL
does, as well as GCL, SBCL, CLISP (albeit only to byte-code),
and many others], then *always* compile your code before worrying
about performance...


-Rob

p.s. Sure, you can cut the runtime even more [87%] by peppering
the code with declarations:

    > (time
	(locally
	  (declare (optimize (speed 3) (safety 0)))
	  (do ((i 1 (+ 1 i)))
	      ((= i 1000) nil)
	    (declare (fixnum i))
	    (do ((j 1 (+ 1 j)))
		((= j 1000) nil)
	      (declare (fixnum j))
	      (the fixnum (+ i j))))))
    ; Compiling LAMBDA NIL: 
    ; Compiling Top-Level Form: 

    ; Evaluation took:
    ;   0.01f0 seconds of real time
    ;   0.001371f0 seconds of user run time
    ;   0.0f0 seconds of system run time
    ;   3,023,496 CPU cycles
    ;   0 page faults and
    ;   0 bytes consed.
    ; 
    NIL
    > 

But why bother in this case?

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607