[gambit-list] Clumsy reference to closure variable

Bradley Lucier lucier at math.purdue.edu
Sat Apr 8 14:56:17 EDT 2006


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



More information about the Gambit-list mailing list