Re: memory usage issue for double-float operations
Vasily Postnicov <[email protected]> Tue, 8 Oct 2024 12:16:30 +0300
| Newsgroups | gmane.lisp.steel-bank.general |
|---|---|
| Message-ID | <CADnZ6BmUuKuA+YD0YdYR6-rDdjnD2CsxDoK2rtYToZZXmE4UOA@mail.gmail.com> |
Wow, great work! I use SBCL for my (near) scientific work quite often and I usually prefer DOUBLE-FLOATs over SINGLE-FLOATs. I guess not consing double precision arguments /return values would be a great improvement. вт, 8 окт. 2024 г., 01:23 Stas Boukarev <[email protected]>: > 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 > _______________________________________________ Sbcl-help mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/sbcl-help