[Sbcl-help] optimization: what to the messages mean? From: Tamas K Papp - 2008-10-19 16:25 ```Hi, I have a function which performs a simulation using functions and values taken from a class. It saves the simulation results in a vector of vectors. I tried to speed it up, but didn't gain much. I have a hard time trying to interpret the messages of SBCL. I include the function and the messages below, could someone please give me a hint? Things I need help with: - what is float to pointer coercion, and how can I get rid of it? - what's SBCL's problem with the first iter? - the functions wage, surplus-share, and inverse-upsilon are closures generated elsewhere, I tried to declare their type to help SBCL, is this the right way? (defun simulate-series (wages &key (n 10000) (burn-in 1000)) "Simulate n draws of #(p beta wage duration tenure source) draws from the life of an agent. Return a vector of lists. Tenure is recorded at the beginning of a period. Source is 2 worker was hired from unemployment, 1 if came from different employer, 0 if state change with same employer." (declare (optimize (speed 3))) (with-slots (wage surplus-share) wages (with-slots (mu s v separation-rate inverse-upsilon) (slot-path wages 'value-functions 'distribution) (declare (double-float mu s v separation-rate) ((function (double-float double-float) double-float) wage surplus-share) ((function (double-float) double-float) inverse-upsilon) ((unsigned-byte 30) n burn-in)) (let* ((sep-rate separation-rate) (offer-rate (* mu s v)) (rates (vector sep-rate offer-rate)) (beta0 (slot-path wages 'value-functions 'beta0)) ;; initial values (p% 0d0) (beta% beta0) (w% (funcall wage p% beta%)) (d% 0d0) (t% 0d0) (source% 2) ;; collected series (pbwdts (make-array n :element-type t)) ;; index on series (j 0)) (declare (double-float p% beta% w% d% t%) ((unsigned-byte 30) j)) (labels (;; draw a firm from the vacancy distribution (draw-firm () (the double-float (funcall inverse-upsilon (random v)))) ;; state change (state-change (p beta w d source record-p) (declare (double-float p beta w d) (fixnum source)) ;; Record a state change. Unless source is zero, ;; reset duration. Unless record-p, it will not be ;; recorded. (when record-p (setf (aref pbwdts j) (vector p% beta% w% (+ d% d) t% source%)) (incf j)) (unless (<= 0 beta 1) (error "recording nonsense: p=~a p%=~a beta=~a beta%=~a w=~a w%=~a d=~a d%=~a source=~a source%=~a~%" p p% beta beta% w w% d d% source source%)) (setf p% p beta% beta w% w d% 0d0 ;; increase tenure only when staying with employer t% (if (zerop source) (+ t% d) 0d0) source% source) (values)) ;; with no change in state, increase tenure and duration (no-change (d) (declare (double-float d)) (incf d% d) (incf t% d) (values)) ;; Generate new draw, update a%, x% and w%, ;; record if record-p. (next-draw (record-p) (bind (((:values dur index) (first-exponential-random rates))) ;; indexes: 0: separation, 1: offer (ecase index ;; separation, exogenous or endogenous ;; -- immediately reemploy (0 (let* ((new-p (draw-firm)) (new-beta beta0)) (state-change new-p new-beta (funcall wage new-p new-beta) dur 2 ; from unemployment record-p))) ;; new offer (1 (let ((new-p (draw-firm))) (if (> new-p p%) ;; new job (let ((new-beta (funcall surplus-share new-p p%))) (state-change new-p new-beta (funcall wage new-p new-beta) dur 1 ; from another employer record-p)) ;; potential wage increase (let ((new-beta (the double-float (funcall surplus-share p% new-p)))) (if (> new-beta beta%) ;; wage increase (state-change p% new-beta (funcall wage p% new-beta) dur 0 ; from same employer record-p) ;; nothing changes (no-change dur)))))))) (values))) (iter (repeat burn-in) (next-draw nil)) (iter (until (= n j)) (next-draw t)) ;; return the series pbwdts))))) Messages: ; compiling file "/tmp/fileqTVPBD.lisp" (written 19 OCT 2008 12:15:55 PM): ; file: /tmp/fileqTVPBD.lisp ; in: DEFUN SIMULATE-SERIES ; (ITERATE:ITER (ITERATE:REPEAT PRODUCTIVITY::BURN-IN) ; (PRODUCTIVITY::NEXT-DRAW NIL)) ; --> LET* BLOCK TAGBODY SETQ THE 1- ; ==> ; (- #:COUNT214 1) ; ; note: unable to ; optimize ; due to type uncertainty: ; The first argument is a REAL, not a FLOAT. ; --> LET* BLOCK TAGBODY IF <= OR LET < IF ; ==> ; (< SB-C::X SB-C::Y) ; ; note: unable to ; open-code FLOAT to RATIONAL comparison ; due to type uncertainty: ; The first argument is a REAL, not a FLOAT. ; --> LET* BLOCK TAGBODY IF <= OR LET IF OR = IF ; ==> ; (= SB-C::X SB-C::Y) ; ; note: unable to ; open-code FLOAT to RATIONAL comparison ; due to type uncertainty: ; The first argument is a REAL, not a FLOAT. ; ; note: unable to open code because: The operands might not be the same type. ; (LABELS ((PRODUCTIVITY::DRAW-FIRM () ; (THE DOUBLE-FLOAT (FUNCALL PRODUCTIVITY::INVERSE-UPSILON #))) ; (PRODUCTIVITY::STATE-CHANGE ; (PRODUCTIVITY::P PRODUCTIVITY::BETA PRODUCTIVITY::W ; PRODUCTIVITY::D PRODUCTIVITY::SOURCE PRODUCTIVITY::RECORD-P) ; (DECLARE ; (DOUBLE-FLOAT PRODUCTIVITY::P PRODUCTIVITY::BETA PRODUCTIVITY::W ; PRODUCTIVITY::D) ; (FIXNUM PRODUCTIVITY::SOURCE)) ; (WHEN PRODUCTIVITY::RECORD-P (SETF # #) (INCF PRODUCTIVITY::J)) ; (UNLESS (<= 0 PRODUCTIVITY::BETA 1) ; (ERROR "recording nonsense: ; p=~a p%=~a beta=~a beta%=~a w=~a w%=~a ; d=~a d%=~a source=~a source%=~a~%" ; PRODUCTIVITY::P PRODUCTIVITY::P% PRODUCTIVITY::BETA ; PRODUCTIVITY::BETA% PRODUCTIVITY::W PRODUCTIVITY::W% ; PRODUCTIVITY::D PRODUCTIVITY::D% PRODUCTIVITY::SOURCE ; PRODUCTIVITY::SOURCE%)) ; (SETF PRODUCTIVITY::P% ; PRODUCTIVITY::P ; PRODUCTIVITY::BETA% ; PRODUCTIVITY::BETA ; PRODUCTIVITY::W% ; PRODUCTIVITY::W ; PRODUCTIVITY::D% ; 0.0d0 ; PRODUCTIVITY::T% ; (IF # # 0.0d0) ; PRODUCTIVITY::SOURCE% ; ...) ; (VALUES)) ; (PRODUCTIVITY::NO-CHANGE (PRODUCTIVITY::D) ; (DECLARE (DOUBLE-FLOAT PRODUCTIVITY::D)) ; (INCF PRODUCTIVITY::D% PRODUCTIVITY::D) ; (INCF PRODUCTIVITY::T% PRODUCTIVITY::D) ; (VALUES)) ; (PRODUCTIVITY::NEXT-DRAW (PRODUCTIVITY::RECORD-P) ; (METABANG.BIND:BIND (#) (ECASE PRODUCTIVITY::INDEX # #)) ; (VALUES))) ; (ITERATE:ITER (ITERATE:REPEAT PRODUCTIVITY::BURN-IN) ; (PRODUCTIVITY::NEXT-DRAW NIL)) ; (ITERATE:ITER (ITERATE:UNTIL (= PRODUCTIVITY::N PRODUCTIVITY::J)) ; (PRODUCTIVITY::NEXT-DRAW T)) ; PRODUCTIVITY::PBWDTS) ; ; note: Return type not fixed values, so can't use known return convention: ; (VALUES DOUBLE-FLOAT &REST T) ; (ITERATE:ITER (ITERATE:REPEAT PRODUCTIVITY::BURN-IN) ; (PRODUCTIVITY::NEXT-DRAW NIL)) ; --> LET* BLOCK TAGBODY IF <= OR LET < IF ; ==> ; (< SB-C::X SB-C::Y) ; ; note: forced to do GENERIC-< (cost 10) ; unable to do inline fixnum comparison (cost 3) because: ; The first argument is a REAL, not a FIXNUM. ; unable to do inline fixnum comparison (cost 4) because: ; The first argument is a REAL, not a FIXNUM. ; etc. ; --> LET* BLOCK TAGBODY SETQ THE 1- ; ==> ; (- #:COUNT214 1) ; ; note: forced to do GENERIC-- (cost 10) ; unable to do inline fixnum arithmetic (cost 1) because: ; The first argument is a REAL, not a FIXNUM. ; The result is a (VALUES REAL &OPTIONAL), not a (VALUES FIXNUM &REST T). ; unable to do inline fixnum arithmetic (cost 2) because: ; The first argument is a REAL, not a FIXNUM. ; The result is a (VALUES REAL &OPTIONAL), not a (VALUES FIXNUM &REST T). ; etc. ; (VECTOR PRODUCTIVITY::SEP-RATE PRODUCTIVITY::OFFER-RATE) ; --> LET PROGN LET LOCALLY SETF SB-KERNEL:%SVSET SB-KERNEL:%ASET ; --> SB-KERNEL:HAIRY-DATA-VECTOR-SET MULTIPLE-VALUE-BIND ; --> MULTIPLE-VALUE-CALL FUNCTION SB-KERNEL:DATA-VECTOR-SET ; ==> ; (SB-KERNEL:DATA-VECTOR-SET-WITH-OFFSET ARRAY SB-INT:INDEX 0 SB-C::NEW-VALUE) ; ; note: doing float to pointer coercion (cost 13), for: ; the third argument of DATA-VECTOR-SET-WITH-OFFSET/SIMPLE-VECTOR ; (SETF PRODUCTIVITY::P% ; PRODUCTIVITY::P ; PRODUCTIVITY::BETA% ; PRODUCTIVITY::BETA ; PRODUCTIVITY::W% ; PRODUCTIVITY::W ; PRODUCTIVITY::D% ; 0.0d0 ; PRODUCTIVITY::T% ; (IF (ZEROP PRODUCTIVITY::SOURCE) (+ PRODUCTIVITY::T% PRODUCTIVITY::D) ; 0.0d0) ; PRODUCTIVITY::SOURCE% ; ...) ; --> PROGN SETF ; ==> ; (SETQ PRODUCTIVITY::T% ; (IF (ZEROP PRODUCTIVITY::SOURCE) (+ PRODUCTIVITY::T% PRODUCTIVITY::D) ; 0.0d0)) ; ; note: doing float to pointer coercion (cost 13), for: ; the second argument of VALUE-CELL-SET ; (VECTOR PRODUCTIVITY::P% PRODUCTIVITY::BETA% PRODUCTIVITY::W% ; (+ PRODUCTIVITY::D% PRODUCTIVITY::D) PRODUCTIVITY::T% ; PRODUCTIVITY::SOURCE%) ; --> LET PROGN LET LOCALLY SETF SB-KERNEL:%SVSET SB-KERNEL:%ASET ; --> SB-KERNEL:HAIRY-DATA-VECTOR-SET MULTIPLE-VALUE-BIND ; --> MULTIPLE-VALUE-CALL FUNCTION SB-KERNEL:DATA-VECTOR-SET ; ==> ; (SB-KERNEL:DATA-VECTOR-SET-WITH-OFFSET ARRAY SB-INT:INDEX 0 SB-C::NEW-VALUE) ; ; note: doing float to pointer coercion (cost 13), for: ; the third argument of DATA-VECTOR-SET-WITH-OFFSET/SIMPLE-VECTOR ; (INCF PRODUCTIVITY::J) ; --> LET* ; ==> ; (SETQ PRODUCTIVITY::J #:G30) ; ; note: doing signed word to integer coercion (cost 20), for: ; the second argument of VALUE-CELL-SET ; (INCF PRODUCTIVITY::D% PRODUCTIVITY::D) ; --> LET* ; ==> ; (SETQ PRODUCTIVITY::D% #:G35) ; ; note: doing float to pointer coercion (cost 13), for: ; the second argument of VALUE-CELL-SET ; (INCF PRODUCTIVITY::T% PRODUCTIVITY::D) ; --> LET* ; ==> ; (SETQ PRODUCTIVITY::T% #:G37) ; ; note: doing float to pointer coercion (cost 13), for: ; the second argument of VALUE-CELL-SET ; (FUNCALL PRODUCTIVITY::INVERSE-UPSILON (RANDOM PRODUCTIVITY::V)) ; ==> ; (SB-C::%FUNCALL FUNCTION #:G134) ; ; note: doing float to pointer coercion (cost 13) ; /tmp/fileqTVPBD.fasl written ; compilation finished in 0:00:00 ; ; compilation unit finished ; printed 14 notes STYLE-WARNING: redefining SIMULATE-SERIES in DEFUN Thanks, Tamas ```

 Re: [Sbcl-help] optimization: what to the messages mean? From: Juho Snellman - 2008-10-19 19:19 ```Tamas K Papp writes: > Hi, > > I have a function which performs a simulation using functions and values > taken from a class. It saves the simulation results in a vector of > vectors. I tried to speed it up, but didn't gain much. I have a hard > time trying to interpret the messages of SBCL. I include the function > and the messages below, could someone please give me a hint? > > Things I need help with: > - what is float to pointer coercion, and how can I get rid of it? Allocating heap space for a float that was previously stored unboxed in a register, or in a specialized float array. This is happening in your code due to: * Storing floats into unspecialized vectors. * Passing floats as arguments to non-inlined functions. * Closing over variables containing floats (not immediately obvious to me why anything is being closed over, however). > - what's SBCL's problem with the first iter? I would guess that iter doesn't declare a type for the iteration variable, and the expansion of the macro is such that sbcl can't automatically infer it either. > - the functions wage, surplus-share, and inverse-upsilon are closures generated > elsewhere, I tried to declare their type to help SBCL, is this the right way? Declaring types doesn't remove the need for boxing the float arguments, only inlining does. -- Juho Snellman ```
 Re: [Sbcl-help] optimization: what to the messages mean? From: Tamas K Papp - 2008-10-26 01:16 ```On Sun, 19 Oct 2008 22:20:27 +0300, Juho Snellman wrote: > Tamas K Papp writes: > >> Hi, >> >> I have a function which performs a simulation using functions and >> values taken from a class. It saves the simulation results in a vector >> of vectors. I tried to speed it up, but didn't gain much. I have a >> hard time trying to interpret the messages of SBCL. I include the >> function and the messages below, could someone please give me a hint? >> >> Things I need help with: >> - what is float to pointer coercion, and how can I get rid of it? > > Allocating heap space for a float that was previously stored unboxed in > a register, or in a specialized float array. This is happening in your > code due to: > > * Storing floats into unspecialized vectors. * Passing floats as > arguments to non-inlined functions. * Closing over variables > containing floats (not immediately obvious to > me why anything is being closed over, however). Hi Juho, Thanks for the answer. I am trying to learn how to optimize in SBCL, and instead of the long function I posted last time, I thought I would start with something simpler. Consider, for demonstrative purposes, (declaim (optimize (speed 3))) (defun foo (x y) (declare (double-float x y)) (values (* x y) (+ x y))) SBCL complains about float to pointer coercion, so I though I would try the specialized vector you suggested: (defun foo1 (x y) (declare (double-float x y)) (make-array 2 :element-type 'double-float :initial-contents (list (* x y) (+ x y)))) But still the same complaint, at the formation of the list. Then I tried (defun foo2 (x y) (declare (double-float x y)) (let ((a (make-array 2 :element-type 'double-float))) (setf (aref a 0) (* x y) (aref a 1) (+ x y)) a)) which now works. It is about 50% faster than the original foo. So is this the way to do it, or is there a neater one (obviously I could write a macro to create the array and put the arguments in it, but I thought something more elegant exists). Thanks, Tamas ```
 Re: [Sbcl-help] optimization: what to the messages mean? From: Nikodemus Siivola - 2008-10-27 20:16 ```On Sun, Oct 26, 2008 at 3:16 AM, Tamas K Papp wrote: > SBCL complains about float to pointer coercion, so I though I would try > the specialized vector you suggested: > > (defun foo1 (x y) > (declare (double-float x y)) > (make-array 2 :element-type 'double-float :initial-contents > (list (* x y) (+ x y)))) > > But still the same complaint, at the formation of the list. Then I tried > > (defun foo2 (x y) > (declare (double-float x y)) > (let ((a (make-array 2 :element-type 'double-float))) > (setf (aref a 0) (* x y) > (aref a 1) (+ x y)) > a)) > > which now works. It is about 50% faster than the original foo. So is > this the way to do it, or is there a neater one (obviously I could write > a macro to create the array and put the arguments in it, but I thought > something more elegant exists). Not really. Currently :INITIAL-CONTENTS suppresses the more important transforms for MAKE-ARRAY. We should really issue a compiler note about that specifically until someone gets around to dealing with :INITIAL-CONTENTS more elegantly. In the meanwhile, for simple generalizing what you are currently doing works. Something like: (defun make-vector (element-type &rest initial-contents) (make-array (length initial-contents) :element-type element-type :initial-contents initial-contents)) (define-compiler-macro make-vector (element-type &rest initial-contents) `(let ((vec (make-array ,(length initial-contents) :element-type ,element-type))) ,@(let ((i -1)) (mapcar (lambda (form) `(setf (aref vec ,(incf i)) ,form)) initial-contents)) vec)) Cheers, -- Nikodemus ```
 Re: [Sbcl-help] optimization: what to the messages mean? From: Thomas F. Burdick - 2008-10-28 16:56 ```2008/10/26 Tamas K Papp : > On Sun, 19 Oct 2008 22:20:27 +0300, Juho Snellman wrote: > >> Tamas K Papp writes: >> >>> Hi, >>> >>> I have a function which performs a simulation using functions and >>> values taken from a class. It saves the simulation results in a vector >>> of vectors. I tried to speed it up, but didn't gain much. I have a >>> hard time trying to interpret the messages of SBCL. I include the >>> function and the messages below, could someone please give me a hint? >>> >>> Things I need help with: >>> - what is float to pointer coercion, and how can I get rid of it? >> >> Allocating heap space for a float that was previously stored unboxed in >> a register, or in a specialized float array. This is happening in your >> code due to: >> >> * Storing floats into unspecialized vectors. * Passing floats as >> arguments to non-inlined functions. * Closing over variables >> containing floats (not immediately obvious to >> me why anything is being closed over, however). > > Hi Juho, > > Thanks for the answer. I am trying to learn how to optimize in SBCL, and > instead of the long function I posted last time, I thought I would start > with something simpler. > > Consider, for demonstrative purposes, > > (declaim (optimize (speed 3))) > > (defun foo (x y) > (declare (double-float x y)) > (values (* x y) (+ x y))) > > SBCL complains about float to pointer coercion, so I though I would try > the specialized vector you suggested: I prefer to use structs for things like this. The combination of good type support for structs (both type checking and the fact that structure slots are adapted to avoid boxing) and boa-constructors makes them more convenient than arrays. (declaim (inline 2double)) (defstruct (2double (:constructor 2double (x y))) (x nil :type double-float) (y nil :type double-float)) (defun foo (x y) (declare (double-float x y)) (2double (* x y) (+ x y)) A little macrology can make this involve less typing, and let you optionally pass in the container struct. Or you can go the route of using compiler macros to cps-convert some of the calls. Or pervasive inlining. ```