From: ········@mac.com
Subject: A question about memory allocation
Date: 
Message-ID: <1105842182.905831.262750@f14g2000cwb.googlegroups.com>
I am a new Lisper and was wondering if anyone could explain to me why
the Lisp compiler is allocating memory for the following function with
LispWorks and openmcl

(declaim (optimize (speed 3) (debug 0) (safety 0) (space 0) (float 0)
(compilation-speed 0)))

(defmacro f- (a b)
`(the single-float (- (the single-float ,a) (the single-float ,b))))

(defun tak (x y z)
(declare (single-float x y z))
(if (not (< y x))
z
(tak (tak (f- x 1.0) y z)
(tak (f- y 1.0) z x)
(tak (f- z 1.0) x y))))

I get the following result:

CL-USER 1 > (time (tak 30.0 20.0 10.0))
Timing the evaluation of (TAK 30.0 20.0 10.0)

user time    =      9.320
system time  =      0.120
Elapsed time =   0:00:12
Allocation   = 607253720 bytes
0 Page faults
Calls to %EVAL    34
11.0

over 600 MB are allocated however if I declare the function with
fixnums like so

(defun tak2 (x y z)
(declare (fixnum x y z))
(if (not (< y x))
z
(tak2 (tak2 (- x 1) y z)
(tak2 (- y 1) z x)
(tak2 (- z 1) x y))))

Timing the evaluation of (TAK2 30 20 10)

user time    =      2.690
system time  =      0.000
Elapsed time =   0:00:03
Allocation   = 8388 bytes
0 Page faults
Calls to %EVAL    34
11

only 8.3 KB is allocated, why would using doubles make a difference
Thanks

Joe

From: Paul F. Dietz
Subject: Re: A question about memory allocation
Date: 
Message-ID: <MfSdnSMiVMMnTHTcRVn-2w@dls.net>
········@mac.com wrote:

> only 8.3 KB is allocated, why would using doubles make a difference

Floating point numbers are normally boxed, which means they're allocated
in small objects on the heap.  Fixnums are typically implemented as immediate
values, indicated by some flag bits in lisp values (the pointers that are passed
around as lisp objects), and involve no heap allocation.

	Paul
From: ········@mac.com
Subject: Re: A question about memory allocation
Date: 
Message-ID: <1105865446.927473.142780@f14g2000cwb.googlegroups.com>
Is there an option to force the Lisp compiler not to box floating point
numbers or is that the standard?

Thanks
From: Barry Margolin
Subject: Re: A question about memory allocation
Date: 
Message-ID: <barmar-EF295E.09412216012005@comcast.dca.giganews.com>
In article <························@f14g2000cwb.googlegroups.com>,
 ·········@mac.com" <········@gmail.com> wrote:

> Is there an option to force the Lisp compiler not to box floating point
> numbers or is that the standard?
> 
> Thanks

Boxing is not a language feature, it's an implementation thing.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Thomas A. Russ
Subject: Re: A question about memory allocation
Date: 
Message-ID: <ymir7ki2pe3.fsf@sevak.isi.edu>
·········@mac.com" <········@gmail.com> writes:

> 
> Is there an option to force the Lisp compiler not to box floating point
> numbers or is that the standard?

More precisely, what is required by the standard is that one be able to
tell that a particular reference is to a floating point numbers.  In
other words, one needs to be able to have (type-of 3.14159) => FLOAT (or
DOUBLE-FLOAT or similar.  It is really up to the implementation how to
arrange for references to floating point numbers to be recognized as
such.

On conventional hardware boxing is one implementation technique
(probably the prevalent one), although perhaps other alternatives
(BiBoP?) could be used.

Now it is possible, with appropriate declarations and compiler settings,
to reduce the amount of boxing of floating point numbers, but the
details and situations where this is possible vary depending on the
particular compiler being used.  In essence what is needed is some
promise (the declarations) that allows the compiler to know the type of
the objects without necessarily being able to test it at run time.



-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal Bourguignon
Subject: Re: A question about memory allocation
Date: 
Message-ID: <873bx26l2f.fsf@thalassa.informatimago.com>
·········@mac.com" <········@gmail.com> writes:

> I am a new Lisper and was wondering if anyone could explain to me why
> the Lisp compiler is allocating memory for the following function with
> LispWorks and openmcl
> 
> (declaim (optimize (speed 3) (debug 0) (safety 0) (space 0) (float 0)
> (compilation-speed 0)))
> 
> (defmacro f- (a b)
> `(the single-float (- (the single-float ,a) (the single-float ,b))))
> 
> (defun tak (x y z)
> (declare (single-float x y z))
> (if (not (< y x))
> z
> (tak (tak (f- x 1.0) y z)
> (tak (f- y 1.0) z x)
> (tak (f- z 1.0) x y))))
> 
> I get the following result:
> 
> CL-USER 1 > (time (tak 30.0 20.0 10.0))
> Timing the evaluation of (TAK 30.0 20.0 10.0)
> 
> user time    =      9.320
> system time  =      0.120
> Elapsed time =   0:00:12
> Allocation   = 607253720 bytes
> 0 Page faults
> Calls to %EVAL    34
> 11.0

Or, choose a better Common Lisp implementation, for example clisp who
has short-float that are unboxed.

(defmacro f- (a b)
  `(the short-float (- (the short-float ,a) (the short-float ,b))))
 

(defun tak (x y z)
  (declare (short-float x y z))
  (if (< y x) 
    (tak (tak (f- x 1.0s0) y z)
         (tak (f- y 1.0s0) z x)
         (tak (f- z 1.0s0) x y))
    z))
;; or (if (>= y x) z (tak ...)) or (if (<= x y) z (tak ...))


[119]> (compile 'tak)
TAK ;
NIL ;
NIL
[120]> (time (tak 30.0s0 20.0s0 10.0s0))

Real time: 139.753 sec.
Run time: 37.55 sec.
Space: 0 Bytes
11.0s0


Yes, it's slower, but "0 Bytes"!  
There is always this space <-> time trade-off.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The rule for today:
Touch my tail, I shred your hand.
New rule tomorrow.
From: Gareth McCaughan
Subject: Re: A question about memory allocation
Date: 
Message-ID: <877jmcq1eh.fsf@g.mccaughan.ntlworld.com>
Pascal Bourguignon wrote:

> Or, choose a better Common Lisp implementation, for example clisp who
> has short-float that are unboxed.
...
> Yes, it's slower, but "0 Bytes"!  
> There is always this space <-> time trade-off.

In this case, there's also a space/accuracy tradeoff.
I can't think of many uses of floats where it wouldn't
be grossly irresponsible to use CLISP's short-floats.
You get ~ 5 decimal digits of precision.

I'd be more inclined to define "a better CL implementation"
as one that can compile code using single-floats or
double-floats to use native floating-point types unboxed.
Which, e.g., CMUCL can do pretty well.

-- 
Gareth McCaughan
.sig under construc
From: Rahul Jain
Subject: Re: A question about memory allocation
Date: 
Message-ID: <87brbofwxm.fsf@nyct.net>
Gareth McCaughan <················@pobox.com> writes:

> I'd be more inclined to define "a better CL implementation"
> as one that can compile code using single-floats or
> double-floats to use native floating-point types unboxed.
> Which, e.g., CMUCL can do pretty well.

If by "code" you mean "a single function" (where inlining doesn't count
as calling another function). The key is to inline your "more primitive"
computational functions into your main one.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: ········@mac.com
Subject: Re: A question about memory allocation
Date: 
Message-ID: <1105955539.553883.319350@z14g2000cwz.googlegroups.com>
I downloaded CMUCL and compiled the code and the compiler gave me the
warning:

; In: DEFUN TAK

;   (DEFUN TAK (X Y Z) (DECLARE # #) (IF # # Z))
; Note: Doing float to pointer coercion (cost 13) from Z to "<return
value>".
;
; Byte Compiling Top-Level Form:

; Compilation unit finished.
;   1 note

Even when I declare the return value to be a float it still gave me the
warning
(defun tak (x y z)
(declare (single-float x y z) (values single-float))
(if (< y x)
(tak (tak (f- x 1.0) y z)
(tak (f- y 1.0) z x)
(tak (f- z 1.0) x y))
z ))

am I doing something wrong with the declare?  Thanks
From: Damien Kick
Subject: Re: A question about memory allocation
Date: 
Message-ID: <acr4kpc6.fsf@email.mot.com>
·········@mac.com" <········@gmail.com> writes:

> I downloaded CMUCL and compiled the code and the compiler gave me the
> warning:
> 
> ; In: DEFUN TAK
> 
> ;   (DEFUN TAK (X Y Z) (DECLARE # #) (IF # # Z))
> ; Note: Doing float to pointer coercion (cost 13) from Z to "<return
> value>".
> ;
> ; Byte Compiling Top-Level Form:
> 
> ; Compilation unit finished.
> ;   1 note
> 
> Even when I declare the return value to be a float it still gave me the
> warning
> (defun tak (x y z)
> (declare (single-float x y z) (values single-float))
> (if (< y x)
> (tak (tak (f- x 1.0) y z)
> (tak (f- y 1.0) z x)
> (tak (f- z 1.0) x y))
> z ))
> 
> am I doing something wrong with the declare?  Thanks

Try this:

    (defun tak (x y z)
      (declare (single-float x y z) (values single-float))
      (the single-float
        (if (< y x)
            (tak (tak (- x 1.0) y z)
                 (tak (- y 1.0) z x)
                 (tak (- z 1.0) x y))
            z)))
From: Thomas A. Russ
Subject: Re: A question about memory allocation
Date: 
Message-ID: <ymiekge3188.fsf@sevak.isi.edu>
Damien Kick <······@email.mot.com> writes:

> 
> ·········@mac.com" <········@gmail.com> writes:
> 
> > I downloaded CMUCL and compiled the code and the compiler gave me the
> > warning:
> > 
> > ; In: DEFUN TAK
> > 
> > ;   (DEFUN TAK (X Y Z) (DECLARE # #) (IF # # Z))
> > ; Note: Doing float to pointer coercion (cost 13) from Z to "<return
> > value>".
> 
> Try this:
> 
>     (defun tak (x y z)
>       (declare (single-float x y z) (values single-float))
>       (the single-float
>         (if (< y x)
>             (tak (tak (- x 1.0) y z)
>                  (tak (- y 1.0) z x)
>                  (tak (- z 1.0) x y))
>             z)))

I suspect this won't change anything.  The return value from TAK will
always need to be boxed for the return value.  Otherwise one would not
be able to invoke TAK in general code or from the top level
Read-Eval-Print loop.  The object returned from the function call needs
to be a real lisp object.

Even if the compiler is clever enough to have alternate entry and return
points for situations where it can avoid the boxing of floats, there
still needs to be a top-level function that does the boxing.


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Duane Rettig
Subject: Re: A question about memory allocation
Date: 
Message-ID: <43bwtmlhy.fsf@franz.com>
···@sevak.isi.edu (Thomas A. Russ) writes:

> Damien Kick <······@email.mot.com> writes:
> 
> > 
> > ·········@mac.com" <········@gmail.com> writes:
> > 
> > > I downloaded CMUCL and compiled the code and the compiler gave me the
> > > warning:
> > > 
> > > ; In: DEFUN TAK
> > > 
> > > ;   (DEFUN TAK (X Y Z) (DECLARE # #) (IF # # Z))
> > > ; Note: Doing float to pointer coercion (cost 13) from Z to "<return
> > > value>".
> > 
> > Try this:
> > 
> >     (defun tak (x y z)
> >       (declare (single-float x y z) (values single-float))
> >       (the single-float
> >         (if (< y x)
> >             (tak (tak (- x 1.0) y z)
> >                  (tak (- y 1.0) z x)
> >                  (tak (- z 1.0) x y))
> >             z)))
> 
> I suspect this won't change anything.  The return value from TAK will
> always need to be boxed for the return value.  Otherwise one would not
> be able to invoke TAK in general code or from the top level
> Read-Eval-Print loop.  The object returned from the function call needs
> to be a real lisp object.

This is true, and even an ftype declaration doesn't have enough
information in it to tell the compiler to in fact bring out
unboxed-style argument passing to the interface.

> Even if the compiler is clever enough to have alternate entry and return
> points for situations where it can avoid the boxing of floats, there
> still needs to be a top-level function that does the boxing.

Heh, You gave me a perfect seque into a little bit of boasting.
(Only a little, though, since the complexity of the argument
manipulation in tak brought out some register allocation bugs
in our implementation).

A top-level boxing must indeed be done, but it need not be
an extra function - it can just be funtionality that is invoked
by virtue of the "lisp-style" entry point being invoked.

We have some undocumented functionality in Allegro CL that we
call "immediate-args" passing, in which a mixture of lisp,
machine-integer (signed), single-float, and double-float
arguments can be passed (and returned) unboxed between lisp
functions. I usually give out the unofficcial documentation
to customers whenever it seems apropos to the problem they
are trying to solve.

The reason why we don't export/document it is because it fails
to be "lispy" in one sense: whenever there is a change of
argument specification, those calls to the changed function
must be recompiled (of course, this is not necessarily
un-lispy, in the face of block-compilation being possible) and
when that recompilation is not done, there is a potential for
segvs to occur (also not necessarily un-lispy, when compilation
is done with low safety) or for gc death to occur (_definitely_
un-lispy, in my opinion; simply redefining a function should
never cause a gc death).  We place a similar statement of caveat
in the unofficial documentation.

Having said that, it is entirely possible to run this tak function
having only boxed up one float at the end of it.  The following
run shows that we get fairly close to this (the three conses
almost certainly have to so with argument passing, and further
testing shows that a compiled function that calls tak does not
use these 3 cells; also, 8 of the 16 other bytes I can't account
for, but am not too concerned about in the face of the more
important issue of the register allocation bugs I saw).  I've
fixed these bugs for the x86 in my development lisp, and now
present this tak run for your enjoyment:

CL-USER(2): (shell "cat tak.cl")

(eval-when (compile)
  (setf (get 'tak 'sys::immed-args-call)
    '((single-float single-float single-float) single-float)))

(declaim (ftype (function (single-float single-float single-float)
			  single-float)
		tak))

(defun tak (x y z)
  (declare (optimize speed (safety 0))
	   (single-float x y z))
  (if (< y x)
      
      (tak (tak (- x 1.0) y z)
	   (tak (- y 1.0) z x)
	   (tak (- z 1.0) x y))
    z))
0
CL-USER(3): :cl tak
;;; Compiling file tak.cl
;;; Writing fasl file tak.fasl
;;; Fasl write complete
; Fast loading /acl/duane/acl71/src/tak.fasl
CL-USER(4): (time (tak 30.0 20.0 10.0))
; cpu time (non-gc) 2,391 msec user, 7 msec system
; cpu time (gc)     0 msec user, 0 msec system
; cpu time (total)  2,391 msec user, 7 msec system
; real time  2,398 msec
; space allocation:
;  3 cons cells, 16 other bytes, 0 static bytes
11.0
CL-USER(5): (time (tak 18.0 12.0 6.0))
; cpu time (non-gc) 8 msec user, 0 msec system
; cpu time (gc)     0 msec user, 0 msec system
; cpu time (total)  8 msec user, 0 msec system
; real time  1 msec
; space allocation:
;  3 cons cells, 16 other bytes, 0 static bytes
7.0
CL-USER(6): 

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: ········@mac.com
Subject: Re: A question about memory allocation
Date: 
Message-ID: <1106724125.691798.20630@f14g2000cwb.googlegroups.com>
I am happy to see that this problem was of some use and was able to
help in finding a bug.  I will download Franz's Lisp from the web site
and give the your advice you gave a try on my second program.  Already
taking the advice and explainations from other users I was able to
reduce the memory allocations by a factor of 3x and improve the speed
by a factor of 4x.  Now my Lisp implemenation runs in 38 seconds while
the C++ version runs in 12.  I think I could do a lot better since I am
allocating a significant amount of memory during the test run.

CL-USER 6 > (time (run 1000000))
Timing the evaluation of (RUN 1000000)

-0.16907516382852447D0
-0.16908618459857994D0
user time    =     18.940
system time  =      0.180
Elapsed time =   0:00:38
Allocation   = 1520058668 bytes
0 Page faults
Calls to %EVAL    32
NIL

If anyone can give me some advice on improving the program I would
greatly appricate it:

(declaim (optimize (speed 3) (debug 0) (safety 0) (float 0)
(space 0) (compilation-speed 0)))

(defmacro f- (one two)
`(the double-float (- (the double-float ,one) (the double-float
,two))))
(defmacro f+ (one two)
`(the double-float (+ (the double-float ,one) (the double-float
,two))))
(defmacro f* (one two)
`(the double-float (* (the double-float ,one) (the double-float
,two))))
(defmacro f/ (one two)
`(the double-float (/ (the double-float ,one) (the double-float
,two))))
(defmacro f-sqrt (one)
`(the double-float (sqrt (the double-float ,one))))
(defmacro f-decf (var val)
`(the double-float (decf (the double-float ,var) (the double-float
,val))))
(defmacro f-incf (var val)
`(the double-float (incf (the double-float ,var) (the double-float
,val))))

(defconstant *PI* 3.141592653589793d0)
(defconstant *SOLAR-MASS* (f* (f* 4.0d0 *PI*) *PI*))
(defconstant *DAYS-PER-YEAR* 365.24d0)
(defconstant *dt* 0.01d0)

(defstruct Body double-float x y z vx vy vz mass run)
(defconstant Jupiter (make-body
:x 4.84143144246472090d+00
:y -1.16032004402742839d+00
:z -1.03622044471123109d-01
:vx (f* 1.66007664274403694d-03 *DAYS-PER-YEAR*)
:vy (f* 7.69901118419740425d-03 *DAYS-PER-YEAR*)
:vz (f* -6.90460016972063023d-05 *DAYS-PER-YEAR*)
:mass (f* 9.54791938424326609d-04 *SOLAR-MASS*)))

(defconstant Saturn (make-body
:x 8.34336671824457987d+00
:y 4.12479856412430479d+00
:z -4.03523417114321381d-01
:vx (f* -2.76742510726862411d-03 *DAYS-PER-YEAR*)
:vy (f* 4.99852801234917238d-03  *DAYS-PER-YEAR*)
:vz (f* 2.30417297573763929d-05 *DAYS-PER-YEAR*)
:mass (* 2.85885980666130812d-04  *SOLAR-MASS*)))

(defconstant Uranus (make-body
:x 1.28943695621391310d+01
:y -1.51111514016986312d+01
:z -2.23307578892655734d-01
:vx (f* 2.96460137564761618d-03 *DAYS-PER-YEAR*)
:vy (f* 2.37847173959480950d-03 *DAYS-PER-YEAR*)
:vz (f* -2.96589568540237556d-05 *DAYS-PER-YEAR*)
:mass (f* 4.36624404335156298d-05 *SOLAR-MASS*)))

(defconstant Neptune (make-body
:x 1.53796971148509165d+01
:y -2.59193146099879641d+01
:z 1.79258772950371181d-01
:vx (f* 2.68067772490389322d-03 *DAYS-PER-YEAR*)
:vy (f* 1.62824170038242295d-03 *DAYS-PER-YEAR*)
:vz (f* -9.51592254519715870d-05 *DAYS-PER-YEAR*)
:mass (f* 5.15138902046611451d-05 *SOLAR-MASS*)))

(defconstant Sun (make-body
:x 0.0d0
:y 0.0d0
:z 0.0d0
:vx 0.0d0
:vy 0.0d0
:vz 0.0d0
:mass *SOLAR-MASS*))

(declaim (inline Offset-Momentum NBodySystem square Energy))

(defun Offset-Momentum(px py pz)
(declare (double-float px py pz))
(make-body
:x 0.0d0
:y 0.0d0
:z 0.0d0
:vx (f/ (f- 0.0d0 px) *SOLAR-MASS*)
:vy (f/ (f- 0.0d0 py) *SOLAR-MASS*)
:vz (f/ (f- 0.0d0 pz) *SOLAR-MASS*)
:mass *SOLAR-MASS*))

(defun NBodySystem(Bodies)
(let ((px 0.0d0)
(py 0.0d0)
(pz 0.0d0)
(pos-i nil))
(declare (double-float px py pz))
(dotimes (i (length Bodies) (+ i 1))
(setq pos-i (svref bodies i))
(f-incf px (f* (body-vx pos-i) (body-mass pos-i)))
(f-incf py (f* (body-vy pos-i) (body-mass pos-i)))
(f-incf pz (f* (body-vz pos-i) (body-mass pos-i))))
(make-body
:x 0.0d0
:y 0.0d0
:z 0.0d0
:vx px
:vy py
:vz pz
:mass *SOLAR-MASS*)))

(defun square (x)
(declare (double-float x))
(f* x x))

(defun energy (Bodies)
(let ((dx 0.0d0)
(dy 0.0d0)
(dz 0.0d0)
(distance 0.0d0)
(e 0.0d0)
(pos-i nil)
(pos-j nil))
(declare (double-float dx dy dz distance e))
(dotimes (i (length bodies) (+ i 1))
(setq pos-i (svref bodies i))
(incf e (f* (f* 0.5d0 (body-mass pos-i))
(f+ (f+ (square (body-vx pos-i))
(square (body-vy pos-i)))
(square (body-vz pos-i)))))
(do ((j (+ i 1) (+ j 1)))
((> j (length bodies)) t)
(when (< j (length bodies))
(setq pos-j (svref bodies j))
(setq dx (f- (body-x pos-i) (body-x pos-j))
dy (f- (body-y pos-i) (body-y pos-j))
dz (f- (body-z pos-i) (body-z pos-j)))
(setq distance (sqrt (f+ (f+ (square dx) (square dy)) (square
dz))))
(f-decf e (f/ (f* (body-mass pos-i) (body-mass pos-j))
distance)))))
e))




(defun run(n)
(let ((Bodies nil)
(nBody nil)
(dx 0.0d0)
(dy 0.0d0)
(dz 0.0d0)
(distance 0.0d0)
(mag 0.0d0)
(vx 0.0d0)
(vy 0.0d0)
(vz 0.0d0)
(x 0.0d0)
(y 0.0d0)
(z 0.0d0)
(pos-i nil)
(pos-j nil))
(declare (fixnum n))
(declare (double-float dx dy dz distance mag vx vy vz))
(setf bodies (vector (copy-structure Sun)
(copy-structure Jupiter)
(copy-structure Saturn)
(copy-structure Uranus)
(copy-structure Neptune)))

(setf nBody (copy-structure (NbodySystem Bodies)))
(setf (svref bodies 0) (Offset-Momentum (body-vx nBody)
(body-vy nBody)
(body-vz nbody)))
(print (energy Bodies))
(dotimes (i n)
(dotimes (i (length Bodies) (+ 1 i))
(do ((j (+ i 1) (+ j 1)))
((> j (length Bodies)) t)
(when (< j (length bodies))
(setq pos-i (svref bodies i)
pos-j (svref bodies j))

(setf dx (f- (body-x pos-i) (body-x pos-j))
dy (f- (body-y pos-i) (body-y pos-j))
dz (f- (body-z pos-i) (body-z pos-j)))
(setq distance (sqrt (f+ (f+ (square dx) (square dy))
(square dz))))

(setf mag (f/ *dt* (f* (f* distance distance) distance))
vx (body-vx pos-i)
vy (body-vy pos-i)
vz (body-vz pos-i))

(f-decf vx (f* (f* dx (body-mass pos-j)) mag))
(f-decf vy (f* (f* dy (body-mass pos-j)) mag))
(f-decf vz (f* (f* dz (body-mass pos-j)) mag))

(setf (body-vx pos-i) vx
(body-vy pos-i) vy
(body-vz pos-i) vz)

(setf vx (body-vx pos-j)
vy (body-vy pos-j)
vz (body-vz pos-j))
(f-incf vx (f* (f* dx (body-mass pos-i)) mag))
(f-incf vy (f* (f* dy (body-mass pos-i)) mag))
(f-incf vz (f* (f* dz (body-mass pos-i)) mag))

(setf (body-vx pos-j) vx
(body-vy pos-j) vy
(body-vz pos-j) vz)
)))

(dotimes (i (length bodies) (+ 1 i))
(setf pos-i (svref bodies i))
(setq x (body-x pos-i)
y (body-y pos-i)
z (body-z pos-i))
(f-incf x (f* *dt* (body-vx pos-i)))
(f-incf y (f* *dt* (body-vy pos-i)))
(f-incf z (f* *dt* (body-vz pos-i)))

(setf (body-x pos-i) x
(body-y pos-i) y
(body-z pos-i) z)
) nil)
    
    (print (energy Bodies)) nil))
From: Duane Rettig
Subject: Re: A question about memory allocation
Date: 
Message-ID: <44qh4a7ls.fsf@franz.com>
·········@mac.com" <········@gmail.com> writes:

> I am happy to see that this problem was of some use and was able to
> help in finding a bug.  I will download Franz's Lisp from the web site
> and give the your advice you gave a try on my second program.

Once you do the download, send mail to me and/or ·······@franz.com
and I'll send you the unofficial documentation.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: rydis (Martin Rydstr|m) @CD.Chalmers.SE
Subject: Re: A question about memory allocation
Date: 
Message-ID: <w4cvf9kdljf.fsf@boris.cd.chalmers.se>
·········@mac.com" <········@gmail.com> writes:
> I am happy to see that this problem was of some use and was able to
> help in finding a bug.  I will download Franz's Lisp from the web site
> and give the your advice you gave a try on my second program.  Already
> taking the advice and explainations from other users I was able to
> reduce the memory allocations by a factor of 3x and improve the speed
> by a factor of 4x.  Now my Lisp implemenation runs in 38 seconds while
> the C++ version runs in 12.  I think I could do a lot better since I am
> allocating a significant amount of memory during the test run.
> 
> CL-USER 6 > (time (run 1000000))
> Timing the evaluation of (RUN 1000000)
> 
> -0.16907516382852447D0
> -0.16908618459857994D0
> user time    =     18.940
> system time  =      0.180
> Elapsed time =   0:00:38
> Allocation   = 1520058668 bytes
> 0 Page faults
> Calls to %EVAL    32
> NIL

I changed the defstruct to be
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defstruct Body 
    double-float
    (x 0d0 :type double-float)
    (y 0d0 :type double-float)
    (z 0d0 :type double-float)
    (vx 0d0 :type double-float)
    (vy 0d0 :type double-float)
    (vz 0d0 :type double-float)
    (mass 0d0 :type double-float)
    run))

(The EVAL-WHEN is for it to be available when DEFCONSTANT is run.)

and tried it in CMUCL-19a on my AMD Duron 1300, 256 Mio ram under
Debian/unstable (the CMUCL is the Debian-packaged one).
---
* (time (run 1000000))
; Compiling LAMBDA NIL: 
; Compiling Top-Level Form: 

-0.16907516382852453d0 
-0.16908618459856514d0 
; Evaluation took:
;   5.66 seconds of real time
;   3.439477 seconds of user run time
;   0.025996 seconds of system run time
;   7,385,502,600 CPU cycles
;   0 page faults and
;   8,984 bytes consed.
; 
NIL
*
---

Then I tried with a version compiled by
(compile-file "jmikhail.lisp" :block-compile t :entry-points '(run)):
---
-0.16907516382852453d0 
-0.16908618459856514d0 
; Evaluation took:
;   3.59 seconds of real time
;   3.450475 seconds of user run time
;   0.002 seconds of system run time
;   4,675,054,942 CPU cycles
;   1 page fault and
;   8,984 bytes consed.
; 
NIL
* 
---

Without the type declarations in the defstruct, it took about 19
seconds with CMUCL. Decent speedup. :)

Regards,

'mr

-- 
[Emacs] is written in Lisp, which is the only computer language that is
beautiful.  -- Neal Stephenson, _In the Beginning was the Command Line_
From: ········@mac.com
Subject: Re: A question about memory allocation
Date: 
Message-ID: <1106794650.814399.265810@f14g2000cwb.googlegroups.com>
I tried to subsitute the code you gave me into my program but it
doesn't work for CMUCL, CLISP, or for LispWorks.  Basicly the error I
get is that make-bodyis already defined.  I make sure that this was the
onlyplace in which the structure was declare.  The error LispWorks
gives is Duplicate slots ( ) provided for defsruct body.
From: Damien Kick
Subject: Re: A question about memory allocation
Date: 
Message-ID: <fz0s93kn.fsf@email.mot.com>
···@sevak.isi.edu (Thomas A. Russ) writes:

> > Try this:
> > 
> >     (defun tak (x y z)
> >       (declare (single-float x y z) (values single-float))
> >       (the single-float
> >         (if (< y x)
> >             (tak (tak (- x 1.0) y z)
> >                  (tak (- y 1.0) z x)
> >                  (tak (- z 1.0) x y))
> >             z)))
> 
> I suspect this won't change anything.  [...]

Yeah, I tried to cancel the post not long after I sent it.  <shrug>
I'd tried out adding (THE SINGLE-FLOAT ...) in a REPL but had
forgotten to use an OPTIMIZE to convince CMUCL to attempt to do
something with the TYPE declarations.  I didn't find any notes not
because the change made a difference but rather because CMUCL wasn't
actually trying to do much.  And this leads me to a question...

I know how to affect compiler optimizations with a DELCARE in the body
of the DEFUN.

    PAIP-USER> (defun foo (x y)
                 (declare (optimize speed (safety 0) (debug 0))
                          (fixnum x y)
                          ;; Using the extention about which I just
                          ;; learned in this thread
                          (values fixnum))
                 (+ x y))
    FOO
    PAIP-USER> (compile 'foo)
    ; Compiling LAMBDA (X Y): 
    ; Compiling Top-Level Form: 

    FOO
    NIL
    NIL
    PAIP-USER> (disassemble 'foo)

    ; In: LAMBDA (X Y)

    ;   #'(LAMBDA (X Y) (DECLARE # # #) (BLOCK FOO #))
    ; Warning: Unknown optimization quality DEBUG in (OPTIMIZE SPEED (SAFETY 0)
    ;                                                 (DEBUG 0)).
    ; 
    ; Compiling LAMBDA (X Y): 
    ; Compiling Top-Level Form: 

    ; Compilation unit finished.
    ;   1 warning

    80873ED0:       .ENTRY "LAMBDA (X Y)"(x y)   ; (FUNCTION (FIXNUM FIXNUM) FIXNUM)
         EE8:       ADD        -18, %CODE
         EEC:       ADD        %CFP, 32, %CSP

         EF0:       ADD        %A1, %A0          ; %A0 = X
                                                 ; %A1 = Y
                                                 ; No-arg-parsing entry point
                                                 ; [:NON-LOCAL-ENTRY]

         EF4:       MOV        %CFP, %CSP        ; [:BLOCK-START]
         EF8:       MOV        %OCFP, %CFP
         EFC:       J          %LRA+5
         F00:       MOV        %LRA, %CODE
         F04:       ILLTRAP    0

    ; No value
    PAIP-USER> 

However, I'm confused as the why the following does not seem to work
the same.

    PAIP-USER> (defun bar (x y)
                 (declare (fixnum x y)
                          (values fixnum))
                 (+ x y))
    BAR
    PAIP-USER> (locally (declare (optimize speed (safety 0)))
                 (compile 'bar))
    ; Compiling LAMBDA (X Y): 
    ; Compiling Top-Level Form: 

    BAR
    NIL
    NIL
    PAIP-USER> (disassemble 'bar)
    808CEF20:       .ENTRY BAR(x y)              ; (FUNCTION (FIXNUM FIXNUM) FIXNUM)
        EF38:       ADD        -18, %CODE
        EF3C:       ADD        %CFP, 32, %CSP

        EF40:       CMP        %NARGS, 8         ; %NARGS = #:G0
                                                 ; [:NON-LOCAL-ENTRY]
        EF44:       BPNE,PN    %ICC, L1
        EF48:       NOP
        EF4C:       ANDCC      %A0, 3, %ZERO     ; %A0 = #:G1
        EF50:       BPNE,PN    %ICC, L2
        EF54:       NOP
        EF58:       ANDCC      %A1, 3, %ZERO     ; %A1 = #:G2
        EF5C:       BPNE,PN    %ICC, L3
        EF60:       NOP
        EF64:       MOV        %A0, %NL2

        EF68:       SRA        %NL2, 2, %NL0     ; %NL2 = X
                                                 ; No-arg-parsing entry point
                                                 ; [:NON-LOCAL-ENTRY]
        EF6C:       SRA        %A1, 2, %NL1      ; %A1 = Y
        EF70:       ADD        %NL1, %NL0

        EF74:       SRL        %NL0, 29, %NL1    ; [:BLOCK-START]
        EF78:       ADD        1, %NL1
        EF7C:       AND        6, %NL1
        EF80:       CMP        %NL1, %ZERO
        EF84:       BPEQ       %ICC, L0
        EF88:       SLL        %NL0, 2, %A0
        EF8C:       OR         4, %ALLOC         ; Set pseudo-atomic flag
        EF90:       OR         %ALLOC, 7, %A0
        EF94:       ADD        8, %ALLOC         ; Allocating 8 bytes
        EF98:       ADD        %ZERO, 266, %NL1  ; Header word BIGNUM-TYPE, size 1?
        EF9C:       ST         %NL1, [%A0-7]
        EFA0:       ST         %NL0, [%A0-3]
        EFA4:       ANDN       4, %ALLOC         ; Reset pseudo-atomic flag
        EFA8:       ANDCC      %ALLOC, 1, %ZERO
        EFAC:       TNE        %ICC, 16          ; Pseudo atomic interrupted trap?
        EFB0: L0:   ANDCC      %A0, 3, %ZERO
        EFB4:       BPNE,PN    %ICC, L4
        EFB8:       NOP
        EFBC:       MOV        %CFP, %CSP
        EFC0:       MOV        %OCFP, %CFP
        EFC4:       J          %LRA+5
        EFC8:       MOV        %LRA, %CODE
        EFCC:       ILLTRAP    0
        EFD0: L1:   ILLTRAP    10                ; Error trap
        EFD4:       BYTE       #x04
        EFD5:       BYTE       #x19              ; INVALID-ARGUMENT-COUNT-ERROR
        EFD6:       BYTE       #xFE, #xED, #x01  ; NARGS
        EFD9:       .ALIGN     4
        EFDC: L2:   ILLTRAP    10                ; Error trap
        EFE0:       BYTE       #x04
        EFE1:       BYTE       #x0A              ; OBJECT-NOT-FIXNUM-ERROR
        EFE2:       BYTE       #xFE, #x0E, #x02  ; A0
        EFE5:       .ALIGN     4
        EFE8: L3:   ILLTRAP    10                ; Error trap
        EFEC:       BYTE       #x04
        EFED:       BYTE       #x0A              ; OBJECT-NOT-FIXNUM-ERROR
        EFEE:       BYTE       #xFE, #x2E, #x02  ; A1
        EFF1:       .ALIGN     4

        EFF4: L4:   ILLTRAP    10                ; Error trap
                                                 ; [:INTERNAL-ERROR]
        EFF8:       BYTE       #x04
        EFF9:       BYTE       #x0A              ; OBJECT-NOT-FIXNUM-ERROR
        EFFA:       BYTE       #xFE, #x0E, #x02  ; A0
        EFFD:       .ALIGN     4
    ; No value
    PAIP-USER> 

Am I simply confused to think that the LOCALLY should affect the
COMPILE of BAR?  I find the following example, and I know that
examples are not normative, in the CLHS
<http://www.lispworks.com/documentation/HyperSpec/Body/s_locall.htm#locally>:

    ;;; This example shows a definition of a function that has a particular set
    ;;; of OPTIMIZE settings made locally to that definition.
     (locally (declare (optimize (safety 3) (space 3) (speed 0)))
       (defun frob (w x y &optional (z (foo x y)))
         (mumble x y z w)))
    =>  FROB

This makes me think that this LOCALLY example should be equivalent to
(DEFUN FROB # (DECLARE ...) ...) but it is not.  What am I missing?
From: Rahul Jain
Subject: Re: A question about memory allocation
Date: 
Message-ID: <87r7kc0ze9.fsf@nyct.net>
Damien Kick <······@email.mot.com> writes:

> Am I simply confused to think that the LOCALLY should affect the
> COMPILE of BAR?

It will affect the compilation (if any) of the call to COMPILE. Surround
the actual code you want optimized with the LOCALLY DECLARE or DECLAIM
it to change the top-level environment. To make it more clear: DECLARE
affects the lexical environment, not the dynamic environment.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: dkixk
Subject: Re: A question about memory allocation
Date: 
Message-ID: <1106847814.893875.114780@c13g2000cwb.googlegroups.com>
Odd, my usual NNTP server has not received this post yet...

Of course... and this is what the CLHS example I quoted was attempting
to show me, if I hadn't been seeing only that for which I had been
hoping to find instead of what was really there.  Thanks for clarifying
that for me.  Something that I don't understand regarding using a
top-level DECLAIM, however, is how to restore the top-level to its old
state.  For example:

(declaim (declare (optimize speed (safety 0))))
(compile 'foo)
(declaim (declare (optimize # #))) ;What values to supply

A while back, I was using a third-party CL package which is heavily
optimized and some bug was generating a signal causing my Lisp to
crash, SIGSEGV or SIGABRT or something like that (I forget).  I went
back into the source code, chaning the SPEED and SAFETY settings to
reinsert the error checking that would help find the cause of the
problem.  However, since all of these settings were being made
lexically within the scope of the DEFUNs of the functions themselves, I
had to hunt down all of the offending optimizations w/ my editor.  It
would've been nice to be able to dynamically affect these settings so
that I could've recompiled them with higher safety for debugging but
then restored them back to their super-fast settings once the problem
had been debugged.  How can one acheive this w/o resorting to too much
ad-hockery?

The following occurs to me as a possiblity:

(defun foo #+life-fast-die-young (declare (optimize speed (safety 0))))
...)

but seems to be a poor choice because it involves fiddling with
*FEATURES*.  Perhaps one could write a macro, e.g. DEFUN*, that would
conditionally expand into a DEFUN with declarations.  An example usage
might look something like the following:

(defun* foo (x y z)
(declare (fixnum x y z)
(equivocate
((> *foo-optimize-level* 4)
(optimize speed (safety 0)))
((= *foo-optimize-level* 3)
(optimize speed (safety 1)))))
(frobnicate x y z))
What do Lispniks do for conditional compiler optimization flags?
From: Damien Kick
Subject: Re: A question about memory allocation
Date: 
Message-ID: <651riqgv.fsf@email.mot.com>
·········@mac.com" <········@gmail.com> writes:

> I downloaded CMUCL and compiled the code and the compiler gave me the
> warning:
> 
> ; In: DEFUN TAK
> 
> ;   (DEFUN TAK (X Y Z) (DECLARE # #) (IF # # Z))
> ; Note: Doing float to pointer coercion (cost 13) from Z to "<return
> value>".
> ;
> ; Byte Compiling Top-Level Form:
> 
> ; Compilation unit finished.
> ;   1 note
> 
> Even when I declare the return value to be a float it still gave me the
> warning
> (defun tak (x y z)
>   (declare (single-float x y z) (values single-float))
>   (if (< y x)
>       (tak (tak (- x 1.0) y z)
>            (tak (- y 1.0) z x)
>            (tak (- z 1.0) x y))
>       z))
> 
> am I doing something wrong with the declare?  Thanks

On a somewhat pedantic note, I apologize if you're really only
interested in the practical issues and aren't interested in the
language lawyer bits, the CLHS describes a declaration identifier
<http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_d.htm#declaration_identifier>
as:

        declaration identifier n. one of the symbols declaration,
        dynamic-extent, ftype, function, ignore, inline, notinline,
        optimize, special, or type; or a symbol which is the name of a
        type; or a symbol which has been declared to be a declaration
        identifier by using a declaration declaration.

And it has the following to say about the symbol VALUES
<http://www.lispworks.com/documentation/HyperSpec/Body/t_values.htm>:

        This type specifier can be used only as the value-type in a
        function type specifier or a the special form. It is used to
        specify individual types when multiple values are
        involved. The &optional and &rest markers can appear in the
        value-type list; they indicate the parameter list of a
        function that, when given to multiple-value-call along with
        the values, would correctly receive those values.

        The symbol * may not be among the value-types. 

        The symbol values is not valid as a type specifier; and,
        specifically, it is not an abbreviation for (values).

Unless I'm reading this incorrectly, I would think that VALUES is not
a valid type specifier to use in a DECLARE form like this.  I would
doubt myself but none less than Erik Naggum seems to have agreed with
my reading in the past
<http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/585760cd624ddc88/bf985ab9ac893f68?q=values+type+specifier&_done=%2Fgroup%2Fcomp.lang.lisp%2Fsearch%3Fgroup%3Dcomp.lang.lisp%26q%3Dvalues+type+specifier%26qt_g%3D1%26&_doneTitle=Back+to+Search&&d#bf985ab9ac893f68>
However, both CMUCL and Allegro CL 6.2, at least, seem to implement
this as an extention.  clisp, however, doesn't:

    [1]> (defun tak (x y z)
      (declare (single-float x y z) (values single-float))
      (if (< y x)
          (tak (tak (- x 1.0) y z)
               (tak (- y 1.0) z x)
               (tak (- z 1.0) x y))
          z))
    TAK
    [2]> (compile 'tak)

    WARNING in TAK :
    Unknown declaration VALUES.
    The whole declaration will be ignored.
    TAK ;
    1 ;
    1
    [3]> 


I would've thought the following would be the (choose 'only 'best) way
to declare the return type of TAK
<http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/585760cd624ddc88/bf985ab9ac893f68?q=values+type+specifier&_done=%2Fgroup%2Fcomp.lang.lisp%2Fsearch%3Fgroup%3Dcomp.lang.lisp%26q%3Dvalues+type+specifier%26qt_g%3D1%26&_doneTitle=Back+to+Search&&d#bf985ab9ac893f68>.

    (declaim (ftype (function (single-float single-float single-float)
                              single-float)
                    tak))
    (defun tak (x y z)
      (if (< y x)
          (tak (tak (- x 1.0) y z)
               (tak (- y 1.0) z x)
               (tak (- z 1.0) x y))
          z))

But that doesn't seem to make a difference in the notes produced, at
least with CMUCL 19a, though I for one can't think of why that would
be the case but IANALispImplementor.
From: Pascal Bourguignon
Subject: Re: A question about memory allocation
Date: 
Message-ID: <87u0pboavw.fsf@thalassa.informatimago.com>
Damien Kick <······@email.mot.com> writes:
> > Even when I declare the return value to be a float it still gave me the
> > warning
> > (defun tak (x y z)
> >   (declare (single-float x y z) (values single-float))
> >   (if (< y x)
> >       (tak (tak (- x 1.0) y z)
> >            (tak (- y 1.0) z x)
> >            (tak (- z 1.0) x y))
> >       z))
> > 
> > am I doing something wrong with the declare?  Thanks
> 
> On a somewhat pedantic note, I apologize if you're really only
> interested in the practical issues and aren't interested in the
> language lawyer bits, the CLHS describes a declaration identifier
> [...]
> Unless I'm reading this incorrectly, I would think that VALUES is not
> a valid type specifier to use in a DECLARE form like this.  I would
> doubt myself but none less than Erik Naggum seems to have agreed with
> my reading in the past

Yes, you would just have to:

    (declaim (declaration values))

for implementations who don't have this implementation specific declaration.

> [...]
> However, both CMUCL and Allegro CL 6.2, at least, seem to implement
> this as an extention.  clisp, however, doesn't:

> [...]
> I would've thought the following would be the (choose 'only 'best) way
> to declare the return type of TAK
> [...]
>     (declaim (ftype (function (single-float single-float single-float)
>                               single-float)
>                     tak))

No. ftype declarations only work for callers, not for the called.
 
> [...]
> But that doesn't seem to make a difference in the notes produced, at
> least with CMUCL 19a, 

Indeed.

It makes the caller generate code specific to pass parameters of the
declared type.  If only I could find references in CLHS or remember
where I recently had this discussion and learned it...

> though I for one can't think of why that would
> be the case but IANALispImplementor.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: Toby Allsopp
Subject: Re: A question about memory allocation
Date: 
Message-ID: <878y6n436j.fsf@candyboy.here>
>>>>> "jmikhail" == ········@mac com <········@gmail.com> writes:

    jmikhail> I downloaded CMUCL and compiled the code and the
    jmikhail> compiler gave me the warning:

    jmikhail> ; In: DEFUN TAK

    jmikhail> ;   (DEFUN TAK (X Y Z) (DECLARE # #) (IF # # Z))
    jmikhail> ; Note: Doing float to pointer coercion (cost 13) from Z to "<return value> ".
    jmikhail> ;
    jmikhail> ; Byte Compiling Top-Level Form:

    jmikhail> ; Compilation unit finished.
    jmikhail> ;   1 note

As Rahul Jain mentioned, CMUCL can only unbox floats within a single
function.  This means that any function that returns a float will
generate that note if compiled for SPEED.

I don't know a lot about how CMUCL works under the covers, but from
looking at disassembly it seems that a compiled function can only
return a 32-bit word (this may be different on 64-bit platforms).
This can hold either an unboxed FIXNUM (probably CHARACTER as well) or
a pointer to a boxed anything else.  It's probably much more
complicated than this, but that explains what you're seeing at least.

So, the solution is to make sure that your function is inlined into
its caller, either by DECLAIMing it INLINE or DECLARE-ing it INLINE
locally in the caller.  You could also make TAK local to its caller
using FLET or LABELS.

    jmikhail> Even when I declare the return value to be a float it
    jmikhail> still gave me the warning
    jmikhail> (defun tak (x y z)
    jmikhail> (declare (single-float x y z) (values single-float))
    jmikhail> (if (< y x)
    jmikhail> (tak (tak (f- x 1.0) y z)
    jmikhail> (tak (f- y 1.0) z x)
    jmikhail> (tak (f- z 1.0) x y))
    jmikhail> z ))

    jmikhail> am I doing something wrong with the declare?  Thanks

Damien Kick and Pascal Bourguignon have pointed out some problems with
it, but the main one is that CMUCL simply cannot return floats from
functions without unboxing them (AFAIK).

Toby.