Re: memory usage issue for double-float operations

Michał Herda | Keepit via Sbcl-help <[email protected]> Tue, 8 Oct 2024 15:19:30 +0000
Newsgroups gmane.lisp.steel-bank.general
Message-ID <DU0PR03MB9423E642C22840B0131C87F9C47E2@DU0PR03MB9423.eurprd03.prod.outlook.com>
Thanks for that optimization. One more question from me: is this going to work automatically with *DERIVE-FUNCTION-TYPES*, or are manual ftype proclamations still necessary in that case?
________________________________
From: Stas Boukarev <[email protected]>
Sent: Tuesday, October 8, 2024 5:17 PM
To: Matt Kaufmann <[email protected]>
Cc: [email protected] <[email protected]>; [email protected] <[email protected]>
Subject: Re: [Sbcl-help] memory usage issue for double-float operations

No block compilation is needed, or any other non-standard feature.

On Tue, Oct 8, 2024 at 6:15 PM Matt Kaufmann <[email protected]<mailto:[email protected]>> wrote:
Thank you so much; very cool!  Is block compilation necessary for this
to work?

Stas Boukarev <[email protected]<mailto:[email protected]>> writes:

> [1:text/plain Hide]
>
> Now unboxed returns also work. Trace works as well. The following no longer
> conses:
>
> (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) (values double-float &optional))
>                 f1))
>
> (defun f1 (v)
>   (+ v v))
>
> (declaim (ftype (function (double-float) *) f2))
>
> (defun f2 (v)
>   (loop for i from 1 to 1000
>         do
>         (setf (aref *ar* 0) (f1 v))))
>
>
> On Tue, Oct 8, 2024 at 12:17 AM Stas Boukarev <[email protected]<mailto:[email protected]>> wrote:
>
>> I just pushed the initial attempt for unboxed arguments, so the following
>> doesn't cons anymore:
>>
>> (declaim (ftype (function (double-float double-float) (values t
>> &optional)) f))
>>
>> (defun f (a b)
>>   (> (+ a b) 3d0))
>>
>> (defun bar (n)
>>   (declare (fixnum n))
>>   (f (* pi n) (* pi (1+ n))))
>>
>>
>> Not consing the return value will come in the near future. #'f works as
>> expected, and incompatible redefinitions behave reasonably. trace doesn't
>> yet work.
>>
>> On Sun, Sep 29, 2024 at 1:46 AM Stas Boukarev <[email protected]<mailto:[email protected]>> wrote:
>>
>>> 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]<mailto:[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]<mailto:[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]<mailto:[email protected]>>
>>>> wrote:
>>>> >
>>>> >> Matt Kaufmann <[email protected]<mailto:[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]<mailto:[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]<mailto:[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]<mailto:[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]<mailto:[email protected]>
>>>> >> >>>>>> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>>>> >> >>
>>>> >> >>
>>>> >> >> _______________________________________________
>>>> >> >> Sbcl-help mailing list
>>>> >> >> [email protected]<mailto:[email protected]>
>>>> >> >> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>>>> >> >
>>>> >> >
>>>> >> > _______________________________________________
>>>> >> > Sbcl-help mailing list
>>>> >> > [email protected]<mailto:[email protected]>
>>>> >> > https://lists.sourceforge.net/lists/listinfo/sbcl-help
>>>> >>
>>>> >>
>>>> >> _______________________________________________
>>>> >> Sbcl-help mailing list
>>>> >> [email protected]<mailto:[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]<mailto:[email protected]>
>>>> > https://lists.sourceforge.net/lists/listinfo/sbcl-help
>>>>
>>>>
>>>> _______________________________________________
>>>> Sbcl-help mailing list
>>>> [email protected]<mailto:[email protected]>
>>>> https://lists.sourceforge.net/lists/listinfo/sbcl-help
>>>>
>>>
>
> [2:text/html Show Save:noname (54kB)]



This e-mail is sent to you from Keepit A/S. Dedicated SaaS Data Protection.
VAT: DK30806883, Per Henrik Lings Allé 4, 7., DK-2100 Copenhagen Ø, Denmark.

This e-mail is sent to you directly and is meant for nobody else. If the e-mail contains personal data that Keepit is responsible for and the e-mail was not meant for you, please do not forward, distribute, or copy, i.e. but return the e-mail to sender. Also, do not send the e-mail to a third party without making sure that you have our prior consent. Distribution of this e-mail to unauthorized receivers may have legal consequences.
If you have received an e-mail from us and you don’t know why, then please refer to our Privacy Policy<https://www.keepit.com/privacy-policy/>.
If you do not want to receive more e-mails from us, please contact [email protected]<mailto:[email protected]>.

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