Re: memory usage issue for double-float operations

Stas Boukarev <[email protected]> Tue, 8 Oct 2024 18:03:13 +0300
Newsgroups gmane.lisp.steel-bank.general
Message-ID <CAF63=117Aeu9s9GxNsuGQWDVvtj2fHR-wLO_eTFYHZmMWRheDg@mail.gmail.com>
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]> 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]> 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]>
>> 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
>>>
>>

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