Re: memory usage issue for double-float operations

Matt Kaufmann <[email protected]> Sun, 29 Sep 2024 16:43:58 -0500
Newsgroups gmane.lisp.steel-bank.general
Message-ID <[email protected]>
Thank you for your replies; I'll reply to each point in turn.  In
short, I still don't know how to avoid consing with block compilation,
for functions that return double-floats (but I know that I can use
inlining or arrays).

1. I tried explicit blocks as you suggest, where before compiling I
evaluated
(setq *block-compile-default* :specified)
to avoid a warning.  But there was the same consing.  I don't think
the blocks are supposed to be necessary anyhow when
*block-compile-default* is t.  I also tried
(compile-file "bar.lsp" :block-compile t :entry-points nil)
but that also didn't help to avoid consing.

2. Yes, inlining works as I'd mentioned, and it may be necessary as
noted by replies on this list.  But I was following up on what Stas
Boukarev suggested, that I "might try block compilation instead".

3. I understand that my use of THE and DECLARE may have been
excessive, but it seemed harmless and I was trying to rule out the
possibility of missing one that was necessary.

4. Good point, but I need to stick to double precision.

Thanks,
Matt
Vasily Postnicov <[email protected]> writes:

> [1:text/plain Hide]
>
> 1. There are no blocks in that file. Check CMUCL manual about it:
> https://cmucl.org/docs/cmu-user/html/Block-Compilation.html
> FIXME: SBCL manual has little information about it (yet). To keep it short:
> you start a block with (DECLAIM (SB-EXT:START-BLOCK ...)) and end it with
> (DECLAIM SB-EXT:END-BLOCK)
> 2. There is no way you can avoid consing when returning a double precision
> floating point number. From what DISASSEMBLE tells me, SBCL returns the
> first value in RDX, and it's only 64 bits wide. It simply cannot hold a
> DOUBLE-FLOAT + tag. By inlining, though, you can avoid tagging (as long as
> you are inside an uninlined callee).
> 3. Your use of THE and DECLARE seems to be extremely redundant. SBCL
> already can guess types from FTYPE declarations.
> 4. If you can, stick to single precision. A single precision number can be
> stored in the upper half of RDX, and the lower half will contain the tag
> (#b11001 AFAIK)
>
> вс, 29 сент. 2024 г., 22:27 Matt Kaufmann <[email protected]>:
>
>> Regarding:
>>
>> > You might try block compilation instead.
>>
>> Thank you for the suggestion.  I tried it with the attached file
>> bar.lsp, compiling as follows.
>>
>> (setq *block-compile-default* t)
>> (compile-file "bar.lsp")
>>
>> I didn't understand the warning shown below; I'd appreciate any help
>> eliminating it, or better yet, avoiding consing, because in a new
>> session, I still get "32,768 bytes consed:" with:
>>
>> (load "bar.fasl")
>> (time (f2 1.0d0))
>>
>> Here's the compilation warning.
>>
>> ; in: DEFUN F1
>> ;     (DEFUN F1 (V) (DECLARE (TYPE DOUBLE-FLOAT V)) (THE DOUBLE-FLOAT (+ V
>> V)))
>> ; --> SB-IMPL::%DEFUN SB-IMPL::%DEFUN SB-INT:NAMED-LAMBDA
>> ; ==>
>> ;   #'(SB-INT:NAMED-LAMBDA F1
>> ;         (V)
>> ;       (DECLARE (SB-C::TOP-LEVEL-FORM))
>> ;       (DECLARE (TYPE DOUBLE-FLOAT V))
>> ;       (BLOCK F1 (THE DOUBLE-FLOAT (+ V V))))
>> ;
>> ; note: doing float to pointer coercion (cost 13) to "<return value>"
>>
>> Thanks,
>> Matt
>> Stas Boukarev <[email protected]> writes:
>>
>> > [1:text/plain Hide]
>> >
>> > It could be improved in the future, but currently it's really not ready
>> for
>> > public use and may change at any time.
>> > You might try block compilation instead.
>> >
>> > On Sun, Sep 29, 2024 at 1:37 AM Matt Kaufmann <[email protected]>
>> > wrote:
>> >
>> >> I very much appreciate all the replies, and I think I understand.
>> >> Thank you, all who replied!
>> >>
>> >> > > That's not directly supported by SBCL's current calling conventions.
>> >> >
>> >> > It is actually supported, but we don't advertise it or make it
>> >> well-behaved
>> >> > when misused.
>> >>
>> >> In our application we can arrange for every function to have a
>> >> suitable ftype proclaimed before it is defined, so that double-floats
>> >> are passed and returned only as indicated by the function's ftype;
>> >> also, every function is defined before it is called.  If these suffice
>> >> to guarantee no "misuse" of support for avoiding boxing, and if there
>> >> is a reasonably simple way to provide that support (like setting some
>> >> global), then I'd like to know how that can work, provided that such
>> >> support is likely to remain available in SBCL.
>> >>
>> >> Thanks,
>> >> Matt
>> >> Stas Boukarev <[email protected]> writes:
>> >>
>> >> > [1:multipart/alternative Hide]
>> >> >
>> >> >
>> >> > [1/1:text/plain Hide]
>> >> >
>> >> >> That's not directly supported by SBCL's current calling conventions.
>> >> >
>> >> > It is actually supported, but we don't advertise it or make it
>> >> well-behaved
>> >> > when misused.
>> >> >
>> >> > On Sun, Sep 29, 2024 at 12:30 AM Christophe Rhodes <[email protected]>
>> >> wrote:
>> >> >
>> >> >> Matt Kaufmann <[email protected]> writes:
>> >> >>
>> >> >> > I'd really like to avoid boxing of double-floats returned by
>> function
>> >> >> > calls.
>> >> >>
>> >> >> Avoiding boxing is, essentially, arranging that there be a known
>> >> >> location for the callee and the caller to cooperate in writing and
>> >> >> reading an unboxed return value, without risking that value being
>> >> >> accidentally interpreted as a tagged lisp object pointer.
>> >> >>
>> >> >> That's not directly supported by SBCL's current calling conventions.
>> >> >> But you can do it yourself: have your function, instead of returning
>> a
>> >> >> double-float, take as an argument a (simple-array double-float (1)),
>> and
>> >> >> write its result there; the caller can read the value from there, and
>> >> >> all should happen without consing:
>> >> >>
>> >> >> (defun f1 (v result)
>> >> >>   (declare (type double-float v)
>> >> >>            (type (simple-array double-float (1)) result))
>> >> >>   (setf (aref result 0) (+ v v))
>> >> >>   (values))
>> >> >>
>> >> >> (defvar *ar* (make-array 1 :element-type 'double-float))
>> >> >> (declaim (type (simple-array double-float (1)) *ar*))
>> >> >>
>> >> >> (defun f2 (v)
>> >> >>   (let ((f1r (make-array 1 :element-type 'double-float)))
>> >> >>     (declare (dynamic-extent f1r))
>> >> >>     (loop repeat 1000
>> >> >>           do (setf (aref *ar* 0) (progn (f1 v f1r) (aref f1r 0))))))
>> >> >>
>> >> >> Christophe
>> >> >>
>> >> >> > Below is a small example that boxes unless the function is
>> >> >> > made inline.  Is there, or could there be, a way to avoid boxing
>> the
>> >> >> > double-float result, other than inlining?  Or should I just accept
>> >> >> > that this is somehow not possible?  (Thanks again, Michał "phoe"
>> >> >> > Herda, for the reply; note that I've done all I could below to
>> avoid
>> >> >> > boxing inputs.)
>> >> >> >
>> >> >> > (declaim (optimize (compilation-speed 0) (debug 0) (speed 3)
>> (space 0)
>> >> >> >                    (safety 0)))
>> >> >> >
>> >> >> > (declaim (type (simple-array double-float (1)) *ar*))
>> >> >> >
>> >> >> > (defvar *ar* (make-array '(1)
>> >> >> >                          :element-type 'double-float
>> >> >> >                          :initial-element 0.0d0))
>> >> >> >
>> >> >> > (declaim (ftype (function (double-float) double-float)
>> >> >> >                 f1))
>> >> >> >
>> >> >> > ; (declaim (inline f1)) ; eliminates consing for (time (f2 1.0d0))
>> >> below
>> >> >> >
>> >> >> > (defun f1 (v)
>> >> >> >   (declare (type double-float v))
>> >> >> >   (the double-float (+ v v)))
>> >> >> >
>> >> >> > (declaim (ftype (function (double-float) *)
>> >> >> >                 f2))
>> >> >> >
>> >> >> > (defun f2 (v)
>> >> >> >   (declare (type double-float v))
>> >> >> >   (loop for i from 1 to 1000
>> >> >> >         do
>> >> >> >         (setf (aref (the (simple-array double-float (1))
>> >> >> >                          *ar*)
>> >> >> >                     0)
>> >> >> >               (the double-float (f1 v)))))
>> >> >> >
>> >> >> > ; 32,768 bytes consed, except 0 if f1 calls are inlined:
>> >> >> > (time (f2 1.0d0))
>> >> >> >
>> >> >> > Matt Kaufmann <[email protected]> writes:
>> >> >> >
>> >> >> >> Very interesting.  I expected the declare form in the defun of
>> >> >> >> my-update --
>> >> >> >>
>> >> >> >>     (declare (type (simple-array double-float (*)) ar)
>> >> >> >>              (type double-float v))
>> >> >> >>
>> >> >> >> -- to avoid boxing.  As another experiment I added the following
>> just
>> >> >> >> before the defun of my-update -- but consing still occurred.
>> >> >> >>
>> >> >> >> (declaim (ftype (function ((simple-array double-float (*)) t
>> >> >> double-float)
>> >> >> >>                           double-float)
>> >> >> >>                 my-update))
>> >> >> >>
>> >> >> >> Apparently those extra efforts don't change your point about
>> boxing,
>> >> >> >> which contradicts what I thought I understood.  I appreciate the
>> >> help!
>> >> >> >>
>> >> >> >> Thanks,
>> >> >> >> Matt
>> >> >> >> Michał "phoe" Herda <[email protected]> writes:
>> >> >> >>
>> >> >> >>> Converting a double float to a function argument requires boxing,
>> >> which
>> >> >> >>> allocates memory. If the function is inlined, there are no
>> >> arguments to
>> >> >> >>> pass, so there can be no consing.
>> >> >> >>>
>> >> >> >>> On 26.09.2024 01:55, Matt Kaufmann wrote:
>> >> >> >>>> Thanks so much!
>> >> >> >>>>
>> >> >> >>>> I see that suitable THE declarations also avoid consing.  But
>> >> >> >>>> inserting a function call can cause consing, whether using THE
>> or
>> >> your
>> >> >> >>>> declaim form, as illustrated below.  I'd be interested in any
>> >> insights
>> >> >> >>>> about why that function call causes consing.
>> >> >> >>>>
>> >> >> >>>> (declaim (optimize (compilation-speed 0) (debug 0) (speed 3)
>> >> (space 0)
>> >> >> >>>>                     (safety 0)))
>> >> >> >>>>
>> >> >> >>>> ; Optional:
>> >> >> >>>> ; (declaim (type (simple-array double-float (5000)) *ar*))
>> >> >> >>>>
>> >> >> >>>> (defvar *ar* (make-array '(5000)
>> >> >> >>>>                           :element-type 'double-float
>> >> >> >>>>                           :initial-element 0.0d0))
>> >> >> >>>>
>> >> >> >>>> (loop with i of-type (integer 0 *) = 0 do
>> >> >> >>>>        (cond ((>= i 5000)
>> >> >> >>>>               (return))
>> >> >> >>>>              (t (progn (setf (aref *ar* i)
>> >> >> >>>>                              (float i 0.0d0))
>> >> >> >>>>                        (setq i (1+ i))))))
>> >> >> >>>>
>> >> >> >>>> (defun foo (ar)
>> >> >> >>>>    (loop with i = 0 do
>> >> >> >>>>          (cond
>> >> >> >>>>           ((>= i 4999) ; <-- for i from 0 to 4999 ...
>> >> >> >>>>            (return))
>> >> >> >>>>           (t (setf (aref (the (simple-array double-float (*))
>> ar)
>> >> >> >>>>                          i)
>> >> >> >>>>                    (the double-float
>> >> >> >>>>                         (+ 1.0d0
>> >> >> >>>>                            (the double-float
>> >> >> >>>>                                 (aref (the (simple-array
>> >> double-float
>> >> >> *) ar)
>> >> >> >>>>                                       i)))))
>> >> >> >>>>              (setq i (1+ i))))))
>> >> >> >>>>
>> >> >> >>>> (time (foo *ar*)) ; 0 bytes consed
>> >> >> >>>>
>> >> >> >>>> ; Optional:
>> >> >> >>>> (declaim (inline my-update))
>> >> >> >>>>
>> >> >> >>>> (defun my-update (ar i v)
>> >> >> >>>>    (declare (type (simple-array double-float (*)) ar)
>> >> >> >>>>             (type double-float v))
>> >> >> >>>>    (setf (aref (the (simple-array double-float (*)) ar)
>> >> >> >>>>                i)
>> >> >> >>>>          v))
>> >> >> >>>>
>> >> >> >>>> (defun foo2 (ar)
>> >> >> >>>>    (loop with i = 0 do
>> >> >> >>>>          (cond
>> >> >> >>>>           ((>= i 4999) ; <-- for i from 0 to 4999 ...
>> >> >> >>>>            (return))
>> >> >> >>>>           (t (my-update ar
>> >> >> >>>>                         i
>> >> >> >>>>                         (the double-float
>> >> >> >>>>                              (+ 1.0d0
>> >> >> >>>>                                 (the double-float
>> >> >> >>>>                                      (aref (the (simple-array
>> >> >> double-float *)
>> >> >> >>>>                                                 ar)
>> >> >> >>>>                                            i)))))
>> >> >> >>>>              (setq i (1+ i))))))
>> >> >> >>>>
>> >> >> >>>> (time (foo2 *ar*)) ; 65,536 bytes consed if not inlined, 0
>> >> otherwise
>> >> >> >>>>
>> >> >> >>>> Thanks,
>> >> >> >>>> Matt
>> >> >> >>>> Michał "phoe" Herda <[email protected]> writes:
>> >> >> >>>>
>> >> >> >>>>> (declaim (type (simple-array double-float (5000)) *ar*))
>> >> >> >>>>>
>> >> >> >>>>> On 25.09.2024 21:44, Matt Kaufmann wrote:
>> >> >> >>>>>> Hi,
>> >> >> >>>>>>
>> >> >> >>>>>> Is there a way to modify the example below so that the final
>> form
>> >> >> does
>> >> >> >>>>>> not allocate memory in SBCL?  (To be fair, I tried several
>> other
>> >> >> Lisps
>> >> >> >>>>>> as well, and all of them allocated memory.)
>> >> >> >>>>>>
>> >> >> >>>>>> (declaim (optimize (compilation-speed 0) (debug 0) (speed 3)
>> >> (space
>> >> >> 0)
>> >> >> >>>>>>                      (safety 0)))
>> >> >> >>>>>>
>> >> >> >>>>>> (defvar *ar* (make-array '(5000)
>> >> >> >>>>>>                            :element-type 'double-float
>> >> >> >>>>>>                            :initial-element 0.0d0))
>> >> >> >>>>>>
>> >> >> >>>>>> (loop with i of-type (integer 0 *) = 0 do
>> >> >> >>>>>>         (cond ((>= i 5000)
>> >> >> >>>>>>                (return))
>> >> >> >>>>>>               (t (progn (setf (aref *ar* i)
>> >> >> >>>>>>                               (float i 0.0d0))
>> >> >> >>>>>>                         (setq i (1+ i))))))
>> >> >> >>>>>>
>> >> >> >>>>>> (defun foo ()
>> >> >> >>>>>>     (loop with i of-type fixnum = 0 do
>> >> >> >>>>>>           (cond
>> >> >> >>>>>>            ((>= i 4999) ; <-- for i from 0 to 4999 ...
>> >> >> >>>>>>             (return))
>> >> >> >>>>>>            (t (setf (aref *ar* i)
>> >> >> >>>>>>                     (the double-float
>> >> >> >>>>>>                          (+ 1.0d0
>> >> >> >>>>>>                             (the double-float (aref *ar*
>> i)))))
>> >> >> >>>>>>               (setq i (the fixnum (1+ i)))))))
>> >> >> >>>>>>
>> >> >> >>>>>> (time (foo))
>> >> >> >>>>>>
>> >> >> >>>>>> Thanks,
>> >> >> >>>>>> Matt
>> >> >> >>>>>>
>> >> >> >>>>>>
>> >> >> >>>>>> _______________________________________________
>> >> >> >>>>>> Sbcl-help mailing list
>> >> >> >>>>>> [email protected]
>> >> >> >>>>>> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>> >> >> >>
>> >> >> >>
>> >> >> >> _______________________________________________
>> >> >> >> Sbcl-help mailing list
>> >> >> >> [email protected]
>> >> >> >> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>> >> >> >
>> >> >> >
>> >> >> > _______________________________________________
>> >> >> > Sbcl-help mailing list
>> >> >> > [email protected]
>> >> >> > https://lists.sourceforge.net/lists/listinfo/sbcl-help
>> >> >>
>> >> >>
>> >> >> _______________________________________________
>> >> >> Sbcl-help mailing list
>> >> >> [email protected]
>> >> >> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>> >> >>
>> >> >
>> >> > [1/2:text/html Show Save:noname (38kB)]
>> >> >
>> >> >
>> >> > [2:text/plain Hide]
>> >> >
>> >> >
>> >> > [3:text/plain Hide]
>> >> >
>> >> > _______________________________________________
>> >> > Sbcl-help mailing list
>> >> > [email protected]
>> >> > https://lists.sourceforge.net/lists/listinfo/sbcl-help
>> >>
>> >>
>> >> _______________________________________________
>> >> Sbcl-help mailing list
>> >> [email protected]
>> >> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>> >>
>> >
>> > [2:text/html Show Save:noname (49kB)]
>> _______________________________________________
>> Sbcl-help mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>>
>
> [2:text/html Show Save:noname (63kB)]


_______________________________________________
Sbcl-help mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-help