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#))))
>>>>> "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
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
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
>>>>> "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
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