Thomas A. Russ <tar@sevak.isi.edu> wrote:
+---------------
| Since this seems to come up over and over again, I wonder if it would be
| worthwhile to add some argument checking to the TIME macro that will
| print some sort of warning/complaint/note if it gets invoked with
| something other than a compiled function call?
+---------------
I'd rather not, since that prohibits typing simple examples at the
REPL. Rather, I think CMUCL [one of the CLs which does run stuff
in the REPL in an interpreter by default] does it the right way.
In CMUCL, TIME does automatically compile its arg:
cmu> (time (dotimes (i 2000000000)))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
; Evaluation took:
; 1.83f0 seconds of real time
; 1.811377f0 seconds of user run time
; 0.002219f0 seconds of system run time
; 4,037,066,749 CPU cycles
; 0 page faults and
; 0 bytes consed.
;
NIL
cmu>
Aside: Yes, that's only 2 CPU cycles/iteration.
But TIME *doesn't* try to recursively compile *everything*,
so if you call an "outside" function, the top-level call is
compiled, yes, but the called function runs in whatever mode
it already was -- interpreted, byte-compiled, or native machine-
code compiled:
cmu> (defun foo () () (dotimes (i 1000000)))
FOO ; An interpreted function.
cmu> (time (foo))
; Compiling LAMBDA NIL: ; Only the call was compiled.
; Compiling Top-Level Form:
; [GC threshold exceeded with 13,261,344 bytes in use. Commencing GC.]
; [GC completed with 1,319,360 bytes retained and 11,941,984 bytes freed.]
; [GC will next occur when at least 13,319,360 bytes are in use.]
; [GC threshold exceeded with 13,328,184 bytes in use. Commencing GC.]
; [GC completed with 1,327,552 bytes retained and 12,000,632 bytes freed.]
; [GC will next occur when at least 13,327,552 bytes are in use.]
; [GC threshold exceeded with 13,337,016 bytes in use. Commencing GC.]
; [GC completed with 1,345,808 bytes retained and 11,991,208 bytes freed.]
; [GC will next occur when at least 13,345,808 bytes are in use.]
; [GC threshold exceeded with 13,351,176 bytes in use. Commencing GC.]
; [GC completed with 1,349,904 bytes retained and 12,001,272 bytes freed.]
; [GC will next occur when at least 13,349,904 bytes are in use.]
; Evaluation took:
; 3.09f0 seconds of real time
; 3.054258f0 seconds of user run time
; 0.0f0 seconds of system run time
; 6,832,567,756 CPU cycles
; [Run times include 0.07f0 seconds GC run time]
; 0 page faults and
; 48,010,608 bytes consed.
;
NIL
cmu>
So that's ~6800 CPU cycles/iteration when run interpreted.
But CMUCL also has a byte-code compiler [though it's not
used by default, since it optimizes size over speed]:
cmu> (let ((c::*byte-compile* t))
(compile 'foo))
; Byte Compiling LAMBDA NIL:
; Byte Compiling Top-Level Form:
FOO
NIL
NIL
cmu> (time (foo))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
; Evaluation took:
; 0.36f0 seconds of real time
; 0.356608f0 seconds of user run time
; 1.3f-4 seconds of system run time
; 795,761,092 CPU cycles
; 0 page faults and
; 0 bytes consed.
;
NIL
cmu>
The byte-coded version runs at ~800 CPU cycles/iteration.
And we already know from above what we'll get from the next one...
[Note: CMUCL won't let you machine-compile a byte-code-compiled
function, so we have to redefine it first.]
cmu> (defun foo () () (dotimes (i 1000000)))
FOO
cmu> (compile *)
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
FOO
NIL
NIL
cmu> (time (foo))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
; Evaluation took:
; 0.0f0 seconds of real time
; 9.07f-4 seconds of user run time
; 6.f-6 seconds of system run time
; 2,010,596 CPU cycles
; 0 page faults and
; 0 bytes consed.
;
NIL
cmu>
Back to 2 CPU cycles/iteration, as with the open-coded
top-level call to TIME.
This is as it should be, IMHO. Simple REPL examples get compiled,
for convenience, but users still have a way to control what kind
of code is being timed if they want to.
-Rob
p.s. Another way to defeat the automatic compilation ny TIME
in CMUCL is to call TIME in a non-null lexical environment:
cmu> (let ((n 1000000))
(time (dotimes (i n))))
Warning: TIME form in a non-null environment, forced to interpret.
Compiling entire form will produce more accurate times.
; [GC threshold exceeded with 13,354,376 bytes in use. Commencing GC.]
; [GC completed with 1,458,880 bytes retained and 11,895,496 bytes freed.]
; [GC will next occur when at least 13,458,880 bytes are in use.]
; [GC threshold exceeded with 13,468,072 bytes in use. Commencing GC.]
; [GC completed with 1,464,848 bytes retained and 12,003,224 bytes freed.]
; [GC will next occur when at least 13,464,848 bytes are in use.]
; [GC threshold exceeded with 13,478,408 bytes in use. Commencing GC.]
; [GC completed with 1,474,912 bytes retained and 12,003,496 bytes freed.]
; [GC will next occur when at least 13,474,912 bytes are in use.]
; [GC threshold exceeded with 13,488,472 bytes in use. Commencing GC.]
; [GC completed with 1,484,976 bytes retained and 12,003,496 bytes freed.]
; [GC will next occur when at least 13,484,976 bytes are in use.]
; Evaluation took:
; 3.12f0 seconds of real time
; 3.081772f0 seconds of user run time
; 0.0f0 seconds of system run time
; 6,885,980,850 CPU cycles
; [Run times include 0.06f0 seconds GC run time]
; 0 page faults and
; 48,010,624 bytes consed.
;
NIL
cmu>
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607