3 (declaim (optimize (speed 3) (safety 1)))
5 (defmacro with-measuring
((var measure-fn
) &body body
)
6 (let ((initial (gensym)))
7 `(let ((,initial
(,measure-fn
)))
9 (setf ,var
(- (,measure-fn
) ,initial
)))))
11 (defmacro measure-time
((var) &body body
)
13 (with-measuring (,var get-internal-real-time
)
15 (setf ,var
(coerce (/ ,var internal-time-units-per-second
)
18 (defmacro measure-conses
((var) &body body
)
19 `(with-measuring (,var total-bytes-allocated
)
22 (defun measure-fn (fn &rest args
)
23 (declare (dynamic-extent args
))
24 (let ((bytes 0) (seconds 0) result
)
25 (measure-time (seconds)
26 (measure-conses (bytes)
27 (setf result
(apply fn args
))))
28 (values seconds bytes result
)))
30 (defmacro measure
(seconds bytes
&body body
)
31 (let ((result (gensym)))
33 (measure-time (,seconds
)
34 (measure-conses (,bytes
)
35 (setf ,result
(progn ,@body
))))
38 (defparameter *benchmark-file
*
39 (asdf:system-relative-pathname
40 'lift
"benchmark-data/benchmarks.log"))
42 (defvar *collect-call-counts
* nil
)
44 (defvar *additional-markers
* nil
)
46 (defvar *profiling-threshold
* nil
)
49 (defun cancel-current-profile (&key force?
)
50 (when (prof::current-profile-actual prof
::*current-profile
*)
52 (assert (member (prof:profiler-status
) '(:inactive
))))
54 (setf prof
::*current-profile
* (prof::make-current-profile
))))
57 (defun current-profile-sample-count ()
58 (ecase (prof::profiler-status
:verbose nil
)
59 ((:inactive
:analyzed
) 0)
61 (slot-value (prof::current-profile-actual prof
::*current-profile
*)
63 (:sampling
(warn "Can't determine count while sampling"))))
65 ;; FIXME -- functionify this!
67 (defmacro with-profile-report
((name style
&key
(log-name *benchmark-file
*)
68 (call-counts-p *collect-call-counts
*))
70 (assert (member style
'(:time
:space
)))
71 `(let ((seconds 0.0) (conses 0) result
)
72 (cancel-current-profile :force? t
)
74 (prof:with-profiling
(:type
,style
:count
,call-counts-p
)
75 (measure seconds conses
,@body
))
76 (ensure-directories-exist ,log-name
)
78 (with-open-file (output ,log-name
80 :if-does-not-exist
:create
82 (with-standard-io-syntax
83 (let ((*print-readably
* nil
))
85 (format output
"\(~11,d ~20,s ~10,s ~10,s ~{~s~^ ~} ~s\)"
86 (date-stamp :include-time? t
) ,name
87 seconds conses
*additional-markers
*
89 (when (> (current-profile-sample-count) 0)
90 (let ((pathname (unique-filename
94 :name
(format nil
"~a-~a-" ,name
,style
))
96 (let ((prof:*significance-threshold
*
97 (or *profiling-threshold
* 0.01)))
98 (format t
"~&Profiling output being sent to ~a" pathname
)
99 (with-open-file (output pathname
101 :if-does-not-exist
:create
103 (format output
"~&Profile data for ~a" ,name
)
104 (format output
"~&Date: ~a"
105 (excl:locale-print-time
(get-universal-time)
106 :fmt
"%B %d, %Y %T" :stream nil
))
107 (format output
"~& Total time: ~,2F; Total space: ~:d \(~:*~d\)"
109 (format output
"~%~%")
110 (when (or (eq :time
,style
)
112 (prof:show-flat-profile
:stream output
)
113 (prof:show-call-graph
:stream output
)
115 (format output
"~%~%Call counts~%")
116 (let ((*standard-output
* output
))
117 (prof:show-call-counts
)))))))))))
120 ;; integrate with LIFT
122 (pushnew :measure
*deftest-clauses
*)
125 :measure
1 :class-def
126 (lambda () (def :measure
))
127 '((setf (def :measure
) (cleanup-parsed-parameter value
)))
129 (pushnew 'measured-test-mixin
(def :superclasses
))
132 (defclass measured-test-mixin
()
133 ((total-conses :initform
0
134 :accessor total-conses
)
135 (total-seconds :initform
0
136 :accessor total-seconds
)))