Subject: Re: How to avoid GC in tight numeric test loop? (CMUCL)
From: rpw3@rpw3.org (Rob Warnock)
Date: Fri, 13 Jun 2008 05:23:16 -0500
Newsgroups: comp.lang.lisp
Message-ID: <rLydnUsv-tQJ18_VnZ2dnUVZ_tbinZ2d@speakeasy.net>
Pascal J. Bourguignon <pjb@informatimago.com> wrote:
+---------------
| jaycx2.3.calrobert@spamgourmet.com.remove (Robert Maas) writes:
| > I'll need to find a way to make a DEFUN-and-COMPILE factory that
| > takes a parameter of array size and automatically builds the correct
| > s-expression for the DEFUN and then EVALs then COMPILEs it. One way
| > is to print the s-expression to a string, substitute the literal
| > text within that string, then READ-FROM-STRING to get the new
| > s-expression. The amazing thing is that in Lisp that monstrosity is
| > actually doable!!
...
| Yes, but all this brutality is really not necessary.  This is Lisp!
| 
| (defparameter *one-random-definition*
|    '(defun one-random ()
|       (the (unsigned-byte 21)
|         (random (the (unsigned-byte 21) %RAND-ARG%)))))
| 
| (loop
|   :for log2-of-max :from 1 :to 20
|   :for rand-arg = (expt 2 log2-of-max)
|   :do (eval (subst rand-arg '%rand-arg% *one-random-definition*))
|   :collect (one-random))
| 
| --> (1 0 0 2 11 1 81 107 19 873 1946 2584 7652 1368 13615 44345 
|      28677 17764 101515 524618) ; for example
+---------------

Yes, this is Lisp, and as I told him in my parallel reply,
"We don' need no stinkin' strings!"  ;-}  ;-}

But the issue at question is GC'ing, and your version doesn't
address those problems, which can -- in CMUCL, at least -- only
be addressed at present (1) by compiling ONE-RANDOM [which your
code didn't], and (2) needing to use a literal numeric arg to
RANDOM if the arg is larger than KERNEL:RANDOM-FIXNUM-MAX [an
internal magic CMUCL constant]. My version [see my parallel reply
in <news:8dmdnc_m0Kv_3c_VnZ2dnUVZ_rrinZ2d@speakeasy.net>] did
handle both of those concerns:

    (defun random-builder (arg)
      (let ((*compile-print* nil))
	(compile 'one-random `(lambda () (random ,arg)))
	(compile 'many-randoms
		 `(lambda () (dotimes (k 50000) (one-random))))
	nil))

    ...[remainder elided]...

and results in a MANY-RANDOMS function that does not cons.

If one wanted to pre-compile a bunch of them and save them away
somewhere [and also avoid polluting the global namespace as *my*
version did!! ;-}  ;-} ], then of course a loop like yours would
certainly be the way to do it... but in CMUCL you can go all the
way to 2^32:

    cmu> (defparameter *many-randoms*
           (loop for log2-of-max from 1 to 32
		 for rand-arg = (expt 2 log2-of-max)
	     collect (let ((*compile-print* nil))
		       (compile 'nil `(lambda ()
					(dotimes (k 50000)
					  (random ,rand-arg)))))
		     into results
	     finally (return (coerce results 'vector))))

    *MANY-RANDOMS*
    cmu> (type-of *many-randoms*)

    (SIMPLE-VECTOR 32)
    cmu> (time (funcall (aref *many-randoms* 31)))
    ; Compiling LAMBDA NIL: 
    ; Compiling Top-Level Form: 

    ; Evaluation took:
    ;   0.0f0 seconds of real time
    ;   8.27f-4 seconds of user run time
    ;   0.0f0 seconds of system run time
    ;   1,821,208 CPU cycles
    ;   0 page faults and
    ;   0 bytes consed.
    ; 
    NIL
    cmu> 


-Rob

p.s. Still ugly. The *real* fix is in "cmucl/src/compiler/float-tran.lisp"
[making args in (INTEGER 1 4294967296) work correctly on X86 platforms],
and awaits the attentions of the maintainers.

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