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