Initial revision
[sbcl.git] / src / code / time.lisp
1 ;;;; low-level time functions
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
11
12 (in-package "SB!IMPL")
13
14 (file-comment
15   "$Header$")
16
17 (defconstant internal-time-units-per-second 100
18   #!+sb-doc
19   "The number of internal time units that fit into a second. See
20   GET-INTERNAL-REAL-TIME and GET-INTERNAL-RUN-TIME.")
21
22 (defconstant micro-seconds-per-internal-time-unit
23   (/ 1000000 internal-time-units-per-second))
24 \f
25 ;;; The base number of seconds for our internal "epoch". We initialize
26 ;;; this to the time of the first call to GET-INTERNAL-REAL-TIME, and
27 ;;; then subtract this out of the result.
28 (defvar *internal-real-time-base-seconds* nil)
29 (declaim (type (or (unsigned-byte 32) null) *internal-real-time-base-seconds*))
30
31 (defun get-internal-real-time ()
32   #!+sb-doc
33   "Return the real time in the internal time format. This is useful for
34   finding elapsed time. See Internal-Time-Units-Per-Second."
35   ;; FIXME: See comment on OPTIMIZE declaration in GET-INTERNAL-RUN-TIME.
36   (declare (optimize (speed 3) (safety 3)))
37   (multiple-value-bind (ignore seconds useconds) (sb!unix:unix-gettimeofday)
38     (declare (ignore ignore) (type (unsigned-byte 32) seconds useconds))
39     (let ((base *internal-real-time-base-seconds*)
40           (uint (truncate useconds
41                           micro-seconds-per-internal-time-unit)))
42       (declare (type (unsigned-byte 32) uint))
43       (cond (base
44              (truly-the (unsigned-byte 32)
45                         (+ (the (unsigned-byte 32)
46                                 (* (the (unsigned-byte 32) (- seconds base))
47                                    internal-time-units-per-second))
48                            uint)))
49             (t
50              (setq *internal-real-time-base-seconds* seconds)
51              uint)))))
52
53 #!-(and sparc svr4)
54 (defun get-internal-run-time ()
55   #!+sb-doc
56   "Return the run time in the internal time format. This is useful for
57   finding CPU usage."
58   (declare (values (unsigned-byte 32)))
59   ;; FIXME: In CMU CL this was (SPEED 3) (SAFETY 0), and perhaps
60   ;; someday it should be again, since overhead here is annoying. But
61   ;; it's even more annoying to worry about this function returning
62   ;; out-of-range values, so while debugging the profiling code,
63   ;; I set it to (SAFETY 3) for now.
64   (declare (optimize (speed 3) (safety 3)))
65   (multiple-value-bind (ignore utime-sec utime-usec stime-sec stime-usec)
66       (sb!unix:unix-fast-getrusage sb!unix:rusage_self)
67     (declare (ignore ignore)
68              (type (unsigned-byte 31) utime-sec stime-sec)
69              ;; (Classic CMU CL had these (MOD 1000000) instead, but
70              ;; at least in Linux 2.2.12, the type doesn't seem to be
71              ;; documented anywhere and the observed behavior is to
72              ;; sometimes return 1000000 exactly.)
73              (type (integer 0 1000000) utime-usec stime-usec))
74     (+ (the (unsigned-byte 32)
75             (* (the (unsigned-byte 32) (+ utime-sec stime-sec))
76                internal-time-units-per-second))
77        (truncate (+ utime-usec stime-usec)
78                  micro-seconds-per-internal-time-unit))))
79
80 #!+(and sparc svr4)
81 (defun get-internal-run-time ()
82   #!+sb-doc
83   "Return the run time in the internal time format. This is useful for
84   finding CPU usage."
85   (declare (values (unsigned-byte 32)))
86   ;; FIXME: See comment on OPTIMIZE declaration in other
87   ;; version of GET-INTERNAL-RUN-TIME.
88   (declare (optimize (speed 3) (safety 3)))
89   (multiple-value-bind (ignore utime stime cutime cstime)
90       (sb!unix:unix-times)
91     (declare (ignore ignore cutime cstime)
92              (type (unsigned-byte 31) utime stime))
93     (the (unsigned-byte 32) (+ utime stime))))
94 \f
95 ;;;; Encode and decode universal times.
96
97 ;;; Returns two values:
98 ;;;  - the minutes west of GMT.
99 ;;;  - T if daylight savings is in effect, NIL if not.
100 (sb!alien:def-alien-routine get-timezone sb!c-call:void
101   (when sb!c-call:long :in)
102   (minutes-west sb!c-call:int :out)
103   (daylight-savings-p sb!alien:boolean :out))
104
105 ;;; Subtract from the returned Internal-Time to get the universal time.
106 ;;; The offset between our time base and the Perq one is 2145 weeks and
107 ;;; five days.
108 (defconstant seconds-in-week (* 60 60 24 7))
109 (defconstant weeks-offset 2145)
110 (defconstant seconds-offset 432000)
111 (defconstant minutes-per-day (* 24 60))
112 (defconstant quarter-days-per-year (1+ (* 365 4)))
113 (defconstant quarter-days-per-century 146097)
114 (defconstant november-17-1858 678882)
115 (defconstant weekday-november-17-1858 2)
116 (defconstant unix-to-universal-time 2208988800)
117
118 (defun get-universal-time ()
119   #!+sb-doc
120   "Returns a single integer for the current time of
121    day in universal time format."
122   (multiple-value-bind (res secs) (sb!unix:unix-gettimeofday)
123     (declare (ignore res))
124     (+ secs unix-to-universal-time)))
125
126 (defun get-decoded-time ()
127   #!+sb-doc
128   "Returns nine values specifying the current time as follows:
129    second, minute, hour, date, month, year, day of week (0 = Monday), T
130    (daylight savings times) or NIL (standard time), and timezone."
131   (decode-universal-time (get-universal-time)))
132
133 (defun decode-universal-time (universal-time &optional time-zone)
134   #!+sb-doc
135   "Converts a universal-time to decoded time format returning the following
136    nine values: second, minute, hour, date, month, year, day of week (0 =
137    Monday), T (daylight savings time) or NIL (standard time), and timezone.
138    Completely ignores daylight-savings-time when time-zone is supplied."
139   (multiple-value-bind (weeks secs)
140       (truncate (+ universal-time seconds-offset)
141                 seconds-in-week)
142     (let* ((weeks (+ weeks weeks-offset))
143            (second NIL)
144            (minute NIL)
145            (hour NIL)
146            (date NIL)
147            (month NIL)
148            (year NIL)
149            (day NIL)
150            (daylight NIL)
151            (timezone (if (null time-zone)
152                          (multiple-value-bind
153                              (ignore minwest dst)
154                              (get-timezone (- universal-time
155                                               unix-to-universal-time))
156                            (declare (ignore ignore))
157                            (setf daylight dst)
158                            minwest)
159                          (* time-zone 60))))
160       (declare (fixnum timezone))
161       (multiple-value-bind (t1 seconds) (truncate secs 60)
162         (setq second seconds)
163         (setq t1 (- t1 timezone))
164         (let* ((tday (if (< t1 0)
165                          (1- (truncate (1+ t1) minutes-per-day))
166                          (truncate t1 minutes-per-day))))
167           (multiple-value-setq (hour minute)
168             (truncate (- t1 (* tday minutes-per-day)) 60))
169           (let* ((t2 (1- (* (+ (* weeks 7) tday november-17-1858) 4)))
170                  (tcent (truncate t2 quarter-days-per-century)))
171             (setq t2 (mod t2 quarter-days-per-century))
172             (setq t2 (+ (- t2 (mod t2 4)) 3))
173             (setq year (+ (* tcent 100) (truncate t2 quarter-days-per-year)))
174             (let ((days-since-mar0 (1+ (truncate (mod t2 quarter-days-per-year)
175                                                  4))))
176               (setq day (mod (+ tday weekday-november-17-1858) 7))
177               (let ((t3 (+ (* days-since-mar0 5) 456)))
178                 (cond ((>= t3 1989)
179                        (setq t3 (- t3 1836))
180                        (setq year (1+ year))))
181                 (multiple-value-setq (month t3) (truncate t3 153))
182                 (setq date (1+ (truncate t3 5))))))))
183       (values second minute hour date month year day
184               daylight
185               (if daylight
186                   (1+ (/ timezone 60))
187                   (/ timezone 60))))))
188
189 (defun pick-obvious-year (year)
190   (declare (type (mod 100) year))
191   (let* ((current-year (nth-value 5 (get-decoded-time)))
192          (guess (+ year (* (truncate (- current-year 50) 100) 100))))
193     (declare (type (integer 1900 9999) current-year guess))
194     (if (> (- current-year guess) 50)
195         (+ guess 100)
196         guess)))
197
198 (defun leap-years-before (year)
199   (let ((years (- year 1901)))
200     (+ (- (truncate years 4)
201           (truncate years 100))
202        (truncate (+ years 300) 400))))
203
204 (defvar *days-before-month*
205   #.(let ((reversed-result nil)
206           (sum 0))
207       (push nil reversed-result)
208       (dolist (days-in-month '(31 28 31 30 31 30 31 31 30 31 30 31))
209         (push sum reversed-result)
210         (incf sum days-in-month))
211       (coerce (nreverse reversed-result) 'simple-vector)))
212
213 (defun encode-universal-time (second minute hour date month year
214                                      &optional time-zone)
215   #!+sb-doc
216   "The time values specified in decoded format are converted to
217    universal time, which is returned."
218   (declare (type (mod 60) second)
219            (type (mod 60) minute)
220            (type (mod 24) hour)
221            (type (integer 1 31) date)
222            (type (integer 1 12) month)
223            (type (or (integer 0 99) (integer 1900)) year)
224            (type (or null rational) time-zone))
225   (let* ((year (if (< year 100)
226                    (pick-obvious-year year)
227                    year))
228          (days (+ (1- date)
229                   (aref *days-before-month* month)
230                   (if (> month 2)
231                       (leap-years-before (1+ year))
232                       (leap-years-before year))
233                   (* (- year 1900) 365)))
234          (hours (+ hour (* days 24))))
235     (if time-zone
236         (+ second (* (+ minute (* (+ hours time-zone) 60)) 60))
237         (let* ((minwest-guess
238                 (nth-value 1
239                            (get-timezone (- (* hours 60 60)
240                                             unix-to-universal-time))))
241                (guess (+ minute (* hours 60) minwest-guess))
242                (minwest
243                 (nth-value 1
244                            (get-timezone (- (* guess 60)
245                                             unix-to-universal-time)))))
246           (+ second (* (+ guess (- minwest minwest-guess)) 60))))))
247 \f
248 ;;;; TIME
249
250 (defmacro time (form)
251   #!+sb-doc
252   "Evaluates the Form and prints timing information on *Trace-Output*."
253   `(%time #'(lambda () ,form)))
254
255 ;;; Try to compile the closure arg to %TIME if it is interpreted.
256 (defun massage-time-function (fun)
257   (cond
258    ((sb!eval:interpreted-function-p fun)
259     (multiple-value-bind (def env-p) (function-lambda-expression fun)
260       (declare (ignore def))
261       (cond
262        (env-p
263         (warn "TIME form in a non-null environment, forced to interpret.~@
264                Compiling entire form will produce more accurate times.")
265         fun)
266        (t
267         (compile nil fun)))))
268    (t fun)))
269
270 ;;; Return all the files that we want time to report.
271 (defun time-get-sys-info ()
272   (multiple-value-bind (user sys faults) (sb!sys:get-system-info)
273     (values user sys faults (get-bytes-consed))))
274
275 ;;; The guts of the TIME macro. Compute overheads, run the (compiled)
276 ;;; function, report the times.
277 (defun %time (fun)
278   (let ((fun (massage-time-function fun))
279         old-run-utime
280         new-run-utime
281         old-run-stime
282         new-run-stime
283         old-real-time
284         new-real-time
285         old-page-faults
286         new-page-faults
287         real-time-overhead
288         run-utime-overhead
289         run-stime-overhead
290         page-faults-overhead
291         old-bytes-consed
292         new-bytes-consed
293         cons-overhead)
294     ;; Calculate the overhead...
295     (multiple-value-setq
296         (old-run-utime old-run-stime old-page-faults old-bytes-consed)
297       (time-get-sys-info))
298     ;; Do it a second time to make sure everything is faulted in.
299     (multiple-value-setq
300         (old-run-utime old-run-stime old-page-faults old-bytes-consed)
301       (time-get-sys-info))
302     (multiple-value-setq
303         (new-run-utime new-run-stime new-page-faults new-bytes-consed)
304       (time-get-sys-info))
305     (setq run-utime-overhead (- new-run-utime old-run-utime))
306     (setq run-stime-overhead (- new-run-stime old-run-stime))
307     (setq page-faults-overhead (- new-page-faults old-page-faults))
308     (setq old-real-time (get-internal-real-time))
309     (setq old-real-time (get-internal-real-time))
310     (setq new-real-time (get-internal-real-time))
311     (setq real-time-overhead (- new-real-time old-real-time))
312     (setq cons-overhead (- new-bytes-consed old-bytes-consed))
313     ;; Now get the initial times.
314     (multiple-value-setq
315         (old-run-utime old-run-stime old-page-faults old-bytes-consed)
316       (time-get-sys-info))
317     (setq old-real-time (get-internal-real-time))
318     (let ((start-gc-run-time *gc-run-time*))
319     (multiple-value-prog1
320         ;; Execute the form and return its values.
321         (funcall fun)
322       (multiple-value-setq
323           (new-run-utime new-run-stime new-page-faults new-bytes-consed)
324         (time-get-sys-info))
325       (setq new-real-time (- (get-internal-real-time) real-time-overhead))
326       (let ((gc-run-time (max (- *gc-run-time* start-gc-run-time) 0)))
327         (format *trace-output*
328                 "~&Evaluation took:~%  ~
329                  ~S second~:P of real time~%  ~
330                  ~S second~:P of user run time~%  ~
331                  ~S second~:P of system run time~%  ~
332 ~@[                 [Run times include ~S second~:P GC run time.]~%  ~]~
333                  ~S page fault~:P and~%  ~
334                  ~S bytes consed.~%"
335                 (max (/ (- new-real-time old-real-time)
336                         (float internal-time-units-per-second))
337                      0.0)
338                 (max (/ (- new-run-utime old-run-utime) 1000000.0) 0.0)
339                 (max (/ (- new-run-stime old-run-stime) 1000000.0) 0.0)
340                 (unless (zerop gc-run-time)
341                   (/ (float gc-run-time)
342                      (float internal-time-units-per-second)))
343                 (max (- new-page-faults old-page-faults) 0)
344                 (max (- new-bytes-consed old-bytes-consed) 0)))))))