Re: memory usage issue for double-float operations
Stas Boukarev <[email protected]>
| Newsgroups | gmane.lisp.steel-bank.general |
|---|---|
| Message-ID | <CAF63=12iFSWENYCmhJ8e-pRhcK10yhhLxQmpmgee8vXJ0Q953A@mail.gmail.com> |
> 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 > _______________________________________________ Sbcl-help mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/sbcl-help