From: Timo Tossavainen
Subject: Consing problem (in CMUCL)
Date: 
Message-ID: <37825AD5.A761DE3A@cs.uta.fi>
I'm having problems with consing, the automatically generated function  
(at the end) must not cons a single byte under any circumstances. I think 
I have all the declarations in place, but CMUCL (18b) still says:

Compiling LAMBDA (N): 

In: LAMBDA (N)
  (SETF (#:|saved-1107| -STATE-)
          #:|state-1108|
        (#:|saved-1109| -STATE-)
          #:|state-1110|
        ...)
--> PROGN SETF LET* MULTIPLE-VALUE-BIND LET FUNCALL 
==>
  (C::%FUNCALL #'(SETF #:|saved-1107|) #:G9 #:G10)
Note: Doing float to pointer coercion (cost 13) from #:|state-1108|.

Compiling Top-Level Form: 

Compilation unit finished.
  1 note


#<Function "LAMBDA (N)" {94796C9}>

This is the only code plugged in to the routine, it loads it's state 
from the generated struct and the output is vector of values. The state 
is saved after executing the update-loop a few times. Also the reason 
cannot be that it is a return value, since the setf returns the last.
I also tried (setf pos (the double-float (+ pos increment))), which
didn't help.

(WHEN (>= POS 128.0d0)
 	(INCF POS -128.0d0))
(SETF OUTPUT (AREF TABLE (TRUNCATE POS)))
(INCF POS INCREMENT)

It's a simple wavetable oscillator (written only to test the 
code-generation),  the variables are replaced with gensyms in the code 
below... I've added some comments to clarify it.

BTW, is using eval on a '(lambda ....) bad style ? I've seen multiple
warnings saying that eval should be avoided, but how about run-time 
code-generation ? What is the best way to clean up a run-time generated 
defstruct (or can it be cleaned up ? (fmakunbound?) I mean the accessors 
and other automatically generated stuff)  Yes, it's absolutely necessary
to use one, as I don't know all the types of variables in advance and 
they  need to be accessed as fast as C structs during computation. The 
program is supposed to compile (from code snippets) and simulate modular 
audio DSP networks in realtime (I hope, gc will be off naturally).
It's a "small" hobby project I'm doing while learning lisp... 

Any help appreciated,

Timo

ps. The code is quick'n dirty, it will be optimized (& fixed) 
when I get it running properly first. Now it compiles and works 
properly but conses (why?)

------

(DEFSTRUCT
  (#21# (:CONC-NAME NIL) (:COPIER NIL) (:PREDICATE NIL)
	(:CONSTRUCTOR #22#))
  (#19# (MAKE-ARRAY 16
		    :ELEMENT-TYPE 'SINGLE-FLOAT
		    :INITIAL-ELEMENT 0.0) ;saved output signal
	:TYPE (SIMPLE-ARRAY SINGLE-FLOAT)) ;saved signal
  (#15# #14# :TYPE #13#) ; table, #13# = (simple-array single-float) 
  (#10# 440.0d0 :TYPE DOUBLE-FLOAT) 	;frequency (not used)
  (#7# 0.0d0 :TYPE DOUBLE-FLOAT)	;increment
  (#4# 0.0d0 :TYPE DOUBLE-FLOAT))	;pos

(LAMBDA (N)
	(DECLARE (OPTIMIZE (SPEED 3) (SAFETY 0)))
	(LET ((-STATE- (NETWORK-SAVED-STATE N))
	      (-VECTOR-SIZE- (NETWORK-VECTOR-SIZE N)))
	     (DECLARE (TYPE #21# -STATE-))
	     (LET ((#5# (#4# -STATE-))		;pos
		   (#8# (#7# -STATE-))		;increment
		   (#11# (#10# -STATE-))	;frequency (not used)
		   (#16# (#15# -STATE-))	;table
		   (#19# (#19# -STATE-)))	;output signal
		  (DECLARE (TYPE DOUBLE-FLOAT #5#)
			   (TYPE DOUBLE-FLOAT #8#)
			   (TYPE DOUBLE-FLOAT #11#)
			   (TYPE #13# #16#) 
			   (TYPE (SIMPLE-ARRAY SINGLE-FLOAT) #19#))
		  (LET ((#20# (AREF #19# (- -VECTOR-SIZE- 1)))) ;output 
		       (DOTIMES (-POS- -VECTOR-SIZE- NIL)
				(LET ()
				     (WHEN (>= #5# 128.0d0) (INCF #5# -128.0d0))
				     (SETF #20# (AREF #16# (TRUNCATE #5#)))
				     (INCF #5# #8#)
				     (SETF (AREF #19# -POS-) (THE SINGLE-FLOAT #20#)))))
		  (SETF (#4# -STATE-) #5#  ;<--- THIS CONSES (saving state of pos)
			(#7# -STATE-) #8#
			(#10# -STATE-) #11#))))

From: Raymond Toy
Subject: Re: Consing problem (in CMUCL)
Date: 
Message-ID: <4nwvwdtp1n.fsf@rtp.ericsson.se>
>>>>> "Timo" == Timo Tossavainen <··@cs.uta.fi> writes:
    Timo> I have all the declarations in place, but CMUCL (18b) still says:

    Timo> Compiling LAMBDA (N): 

    Timo> In: LAMBDA (N)
    Timo>   (SETF (#:|saved-1107| -STATE-)
    Timo>           #:|state-1108|
    Timo>         (#:|saved-1109| -STATE-)
    Timo>           #:|state-1110|
    Timo>         ...)
    --> PROGN SETF LET* MULTIPLE-VALUE-BIND LET FUNCALL 
    Timo> ==>
    Timo>   (C::%FUNCALL #'(SETF #:|saved-1107|) #:G9 #:G10)
    Timo> Note: Doing float to pointer coercion (cost 13) from #:|state-1108|.

    Timo> It's a simple wavetable oscillator (written only to test the 
    Timo> code-generation),  the variables are replaced with gensyms in the code 
    Timo> below... I've added some comments to clarify it.

Non-gensyms would have been easier to read....

    Timo> 		  (SETF (#4# -STATE-) #5#  ;<--- THIS CONSES (saving state of pos)
    Timo> 			(#7# -STATE-) #8#
    Timo> 			(#10# -STATE-) #11#))))

It seems to me that #11# should cause consing because it's being
returned as the value of the function.  What happens if you put a
(values) as the last statement so nothing is returned?

Anyway, here is a simple test:

(declaim (optimize (speed 3)))

(defstruct s
  (inc 0d0 :type double-float)
  (pos 0d0 :type double-float))
  
(defun junk (st)
  (declare (type s st))
  (let ((i (s-inc st))
	(p (s-pos st)))
    (setf (s-inc st) i
	  (s-pos st) p)
    (values)))

This gives no compiler warnings or notes.  However, take out the
(values), and you get a note about float to pointer coercion.

If this isn't it, you may want to ask on ··········@cons.org

Ray
From: Timo Tossavainen
Subject: Re: Consing problem (in CMUCL)
Date: 
Message-ID: <37838439.D5A42670@cs.uta.fi>
Raymond Toy wrote:

> Non-gensyms would have been easier to read....

I know, sorry about that... 
 
>     Timo>                 (SETF (#4# -STATE-) #5#  ;<--- THIS CONSES (saving state of pos)
>     Timo>                       (#7# -STATE-) #8#
>     Timo>                       (#10# -STATE-) #11#))))
> 
> It seems to me that #11# should cause consing because it's being
> returned as the value of the function.  What happens if you put a
> (values) as the last statement so nothing is returned?

I thought of that too so I put a nil after the setf earlier and also 
changed the setf to multiple 2 arg setfs.  I also tried the (values) 
part just now, and it didn't help. It's most likely a type-inference 
thing.

> If this isn't it, you may want to ask on ··········@cons.org

I'll do that if I don't get it working soon. I'm going to rewrite the
produced code and struct using non-gensyms and try to see if I can hack 
it to work. I tried to reproduce the problem in simpler code, but so far
I've been unsuccesful.
	
Timo
From: Martin Simmons
Subject: Re: Consing problem (in CMUCL)
Date: 
Message-ID: <01bec887$24bb08d0$35ee58c0@cutler>
Timo Tossavainen <··@cs.uta.fi> wrote in article
<·················@cs.uta.fi>...
> Raymond Toy wrote:
> 
> > Non-gensyms would have been easier to read....
> 
> I know, sorry about that... 
>  
> >     Timo>                 (SETF (#4# -STATE-) #5#  ;<--- THIS CONSES
(saving state of pos)
> >     Timo>                       (#7# -STATE-) #8#
> >     Timo>                       (#10# -STATE-) #11#))))
> > 
> > It seems to me that #11# should cause consing because it's being
> > returned as the value of the function.  What happens if you put a
> > (values) as the last statement so nothing is returned?
> 
> I thought of that too so I put a nil after the setf earlier and also 
> changed the setf to multiple 2 arg setfs.  I also tried the (values) 
> part just now, and it didn't help. It's most likely a type-inference 
> thing.

I don't know about CMUCL, but in many Lisp implementations the :TYPE
option to defstruct slots has no effect on how the slot is held in
memory.  Typically, all slots are stored as general lisp values so a
float will be boxed prior to calling a defstruct setter function.

A common solution to this problem is to store all the float state in a
separate float array (which implementations are more likely to optimize).

__Martin
From: Raymond Toy
Subject: Re: Consing problem (in CMUCL)
Date: 
Message-ID: <4n7loc1mv3.fsf@rtp.ericsson.se>
>>>>> "Martin" == Martin Simmons <······@harlequin.co.uk> writes:

    Martin> I don't know about CMUCL, but in many Lisp implementations the :TYPE
    Martin> option to defstruct slots has no effect on how the slot is held in
    Martin> memory.  Typically, all slots are stored as general lisp values so a
    Martin> float will be boxed prior to calling a defstruct setter function.

This is one of the nice things about CMUCL:  slots don't have to be
boxed up for certains types of slots.  I think this includes
(signed-byte 32), single-float, and double-float.  It may also be true 
for (complex single-float) and (complex double-float), but I'm not
sure.

Ray
From: Timo Tossavainen
Subject: Re: Consing problem (in CMUCL)
Date: 
Message-ID: <37836938.DBE3E62D@cs.uta.fi>
The manual rewrite not using gensyms compiles it without consing 
as does compiling and eval'ing the new rewrite as a list. I'm 
thinking that it might be the shared substructures of the generated 
list that are the culprits.

Timo