Re: memory usage issue for double-float operations
Christophe Rhodes <[email protected]>
| Newsgroups | gmane.lisp.steel-bank.general |
|---|---|
| Message-ID | <[email protected]> |
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