I don't know if this is important in practice, but ...
In lib/_num.scm you have
(define (advance-state!) (##declare (not interrupts-enabled)) (let* ((x10 (##flonum.- (##flonum.* 1403580.0 (##f64vector-ref state 1)) (##flonum.* 810728.0 (##f64vector-ref state 2)))) (y10 (##flonum.- x10 (##flonum.* (##flonum.floor (##flonum./ x10 (macro-m1- inexact))) (macro-m1-inexact)))) (x20 (##flonum.- (##flonum.* 527612.0 (##f64vector-ref state 3)) (##flonum.* 1370589.0 (##f64vector-ref state 5)))) (y20 (##flonum.- x20 (##flonum.* (##flonum.floor (##flonum./ x20 (macro-m2- inexact))) (macro-m2-inexact)))) (dx (##flonum.- y10 y20)) (dy (##flonum.- dx (##flonum.* (##flonum.floor (##flonum./ dx (macro-m1- inexact))) (macro-m1-inexact))))) (##f64vector-set! state 5 (##f64vector-ref state 4)) (##f64vector-set! state 4 (##f64vector-ref state 3)) (##f64vector-set! state 3 y20) (##f64vector-set! state 2 (##f64vector-ref state 1)) (##f64vector-set! state 1 (##f64vector-ref state 0)) (##f64vector-set! state 0 y10) (##f64vector-set! state 6 dy)))
where state is declared in the surrounding lexical environment. This is compiled to the C code:
___DEF_GLBL(___L199__23__23_make_2d_random_2d_source_2d_mrg32k3a) ___SET_R2(___UNBOX(___R1)) ___SET_F64(___F64V1,___F64VECTORREF(___R2,___FIX(2L))) ___SET_F64(___F64V2,___F64MUL(810728.,___F64V1)) ___SET_R3(___UNBOX(___R1)) ___SET_F64(___F64V3,___F64VECTORREF(___R3,___FIX(1L))) ___SET_F64(___F64V4,___F64MUL(1403580.,___F64V3)) ___SET_F64(___F64V5,___F64SUB(___F64V4,___F64V2)) ___SET_F64(___F64V6,___F64DIV(___F64V5,4294967087.)) ___SET_F64(___F64V7,___F64FLOOR(___F64V6)) ___SET_F64(___F64V8,___F64MUL(___F64V7,4294967087.)) ___SET_F64(___F64V9,___F64SUB(___F64V5,___F64V8)) ___SET_R3(___UNBOX(___R1)) ___SET_F64(___F64V10,___F64VECTORREF(___R3,___FIX(5L))) ___SET_F64(___F64V11,___F64MUL(1370589.,___F64V10)) ___SET_R4(___UNBOX(___R1)) ___SET_F64(___F64V12,___F64VECTORREF(___R4,___FIX(3L))) ___SET_F64(___F64V13,___F64MUL(527612.,___F64V12)) ___SET_F64(___F64V14,___F64SUB(___F64V13,___F64V11)) ___SET_F64(___F64V15,___F64DIV(___F64V14,4294944443.)) ___SET_F64(___F64V16,___F64FLOOR(___F64V15)) ___SET_F64(___F64V17,___F64MUL(___F64V16,4294944443.)) ___SET_F64(___F64V18,___F64SUB(___F64V14,___F64V17)) ___SET_F64(___F64V19,___F64SUB(___F64V9,___F64V18)) ___SET_F64(___F64V20,___F64DIV(___F64V19,4294967087.)) ___SET_F64(___F64V21,___F64FLOOR(___F64V20)) ___SET_F64(___F64V22,___F64MUL(___F64V21,4294967087.)) ___SET_F64(___F64V23,___F64SUB(___F64V19,___F64V22)) ___SET_STK(1,___UNBOX(___R1)) ___SET_F64(___F64V24,___F64VECTORREF(___STK(1),___FIX(4L))) ___SET_STK(2,___UNBOX(___R1)) ___F64VECTORSET(___STK(2),___FIX(5L),___F64V24) ___SET_STK(1,___UNBOX(___R1)) ___SET_F64(___F64V25,___F64VECTORREF(___STK(1),___FIX(3L))) ___SET_STK(2,___UNBOX(___R1)) ___F64VECTORSET(___STK(2),___FIX(4L),___F64V25) ___SET_STK(1,___UNBOX(___R1)) ___F64VECTORSET(___STK(1),___FIX(3L),___F64V18) ___SET_R3(___UNBOX(___R1)) ___SET_F64(___F64V26,___F64VECTORREF(___R3,___FIX(1L))) ___SET_STK(1,___UNBOX(___R1)) ___F64VECTORSET(___STK(1),___FIX(2L),___F64V26) ___SET_R3(___UNBOX(___R1)) ___SET_F64(___F64V27,___F64VECTORREF(___R3,___FIX(0L))) ___SET_STK(1,___UNBOX(___R1)) ___F64VECTORSET(___STK(1),___FIX(1L),___F64V27) ___SET_R3(___UNBOX(___R1)) ___F64VECTORSET(___R3,___FIX(0L),___F64V9) ___SET_R1(___UNBOX(___R1)) ___F64VECTORSET(___R1,___FIX(6L),___F64V23) ___JUMPPRM(___NOTHING,___R0)
Note how state is continually unboxed into new registers and stack entries. If I change the argument list to
(define (advance-state! state) <etc>
and update all callers, the new code is
___DEF_GLBL(___L199__23__23_make_2d_random_2d_source_2d_mrg32k3a) ___SET_F64(___F64V1,___F64VECTORREF(___R1,___FIX(2L))) ___SET_F64(___F64V2,___F64MUL(810728.,___F64V1)) ___SET_F64(___F64V3,___F64VECTORREF(___R1,___FIX(1L))) ___SET_F64(___F64V4,___F64MUL(1403580.,___F64V3)) ___SET_F64(___F64V5,___F64SUB(___F64V4,___F64V2)) ___SET_F64(___F64V6,___F64DIV(___F64V5,4294967087.)) ___SET_F64(___F64V7,___F64FLOOR(___F64V6)) ___SET_F64(___F64V8,___F64MUL(___F64V7,4294967087.)) ___SET_F64(___F64V9,___F64SUB(___F64V5,___F64V8)) ___SET_F64(___F64V10,___F64VECTORREF(___R1,___FIX(5L))) ___SET_F64(___F64V11,___F64MUL(1370589.,___F64V10)) ___SET_F64(___F64V12,___F64VECTORREF(___R1,___FIX(3L))) ___SET_F64(___F64V13,___F64MUL(527612.,___F64V12)) ___SET_F64(___F64V14,___F64SUB(___F64V13,___F64V11)) ___SET_F64(___F64V15,___F64DIV(___F64V14,4294944443.)) ___SET_F64(___F64V16,___F64FLOOR(___F64V15)) ___SET_F64(___F64V17,___F64MUL(___F64V16,4294944443.)) ___SET_F64(___F64V18,___F64SUB(___F64V14,___F64V17)) ___SET_F64(___F64V19,___F64SUB(___F64V9,___F64V18)) ___SET_F64(___F64V20,___F64DIV(___F64V19,4294967087.)) ___SET_F64(___F64V21,___F64FLOOR(___F64V20)) ___SET_F64(___F64V22,___F64MUL(___F64V21,4294967087.)) ___SET_F64(___F64V23,___F64SUB(___F64V19,___F64V22)) ___SET_F64(___F64V24,___F64VECTORREF(___R1,___FIX(4L))) ___F64VECTORSET(___R1,___FIX(5L),___F64V24) ___SET_F64(___F64V25,___F64VECTORREF(___R1,___FIX(3L))) ___F64VECTORSET(___R1,___FIX(4L),___F64V25) ___F64VECTORSET(___R1,___FIX(3L),___F64V18) ___SET_F64(___F64V26,___F64VECTORREF(___R1,___FIX(1L))) ___F64VECTORSET(___R1,___FIX(2L),___F64V26) ___SET_F64(___F64V27,___F64VECTORREF(___R1,___FIX(0L))) ___F64VECTORSET(___R1,___FIX(1L),___F64V27) ___F64VECTORSET(___R1,___FIX(0L),___F64V9) ___F64VECTORSET(___R1,___FIX(6L),___F64V23) ___JUMPPRM(___NOTHING,___R0)
Benchmarking shows that in this case it makes little difference (about 10%), perhaps because of the overhead of the glue surrounding the random-number generator. Before the change, simple fixnum loops (the number of iterations is the parameter) take:
(time (test-integers 1000000))
(time (test-integers 1000000)) 230 ms real time 230 ms cpu time (230 user, 0 system) no collections -16 bytes allocated 2 minor faults no major faults 81
(time (test-integers 1000000))
(time (test-integers 1000000)) 231 ms real time 231 ms cpu time (231 user, 0 system) no collections -16 bytes allocated no minor faults no major faults 67
(time (test-reals 1000000))
(time (test-reals 1000000)) 220 ms real time 219 ms cpu time (210 user, 9 system) no collections 32000016 bytes allocated 3907 minor faults no major faults .6123283969620956
(time (test-reals 1000000))
(time (test-reals 1000000)) 221 ms real time 221 ms cpu time (209 user, 12 system) no collections 32000016 bytes allocated 3906 minor faults no major faults .7141848196160148
after:
(time (test-integers 1000000))
(time (test-integers 1000000)) 206 ms real time 205 ms cpu time (205 user, 0 system) no collections -16 bytes allocated 3 minor faults no major faults 81
(time (test-integers 1000000))
(time (test-integers 1000000)) 203 ms real time 203 ms cpu time (203 user, 0 system) no collections -16 bytes allocated no minor faults no major faults 67
(time (test-reals 1000000))
(time (test-reals 1000000)) 197 ms real time 197 ms cpu time (187 user, 10 system) no collections 32000016 bytes allocated 3906 minor faults no major faults .6123283969620956
(time (test-reals 1000000))
(time (test-reals 1000000)) 203 ms real time 204 ms cpu time (189 user, 15 system) no collections 32000016 bytes allocated 3906 minor faults no major faults .7141848196160148
while a loop that just makes calls to (values 0) through the trampoline takes:
(time (test-empty 1000000))
(time (test-empty 1000000)) 35 ms real time 35 ms cpu time (35 user, 0 system) no collections -16 bytes allocated no minor faults no major faults
and a loop that does absolutely nothing except increment and test the loop variable takes
(time (test-really-empty 1000000))
(time (test-really-empty 1000000)) 1 ms real time 1 ms cpu time (1 user, 0 system) no collections -16 bytes allocated no minor faults no major faults 0
It does make a significant difference in the code size:
euler-42% ll `which gsc` -rwxr-xr-x 1 lucier pfaculty 7525990 Feb 2 00:57 /pkgs/Gambit-C/bin/ gsc* euler-43% ll gsc/gsc -rwxr-xr-x 1 lucier pfaculty 7464437 Apr 8 13:59 gsc/gsc*
It would be nice if the compiler would just unbox the closure variable once and we wouldn't have to worry about this.
Brad