I've extracted a piece of a language implementation here (also attached)
https://gist.github.com/wilbowma/87d7e18718e08968cc4b2d003efbff2b
It provides two implementations of a little language that provides an unbounded
number of implicit global mutable variables.
I decided to bind the first 1000 of them because I figure no user would generate
that many, instead of doing anything clever.
One method uses `let-syntax` and implements these variables with set!
transformers, and the other just binds the variables with `let`.
It seems the version that uses `let-syntax` runs in time directly proportional
to the number of these variables I decide to bind, i.e., the number of macros I
introduce via `let-syntax`; about 1ms per variable bound, regardless of whether
the macros are ever used.
This behaviour surprised me a lot. Is this expected?
--
William J. Bowman
--
You received this message because you are subscribed to the Google Groups
"Racket Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-dev/YirU6gt5MxSnLcvf%40williamjbowman.com.
#lang racket
(require (for-syntax racket/syntax))
#|
1,000 fvars; let-syntax implementation
cpu time: 1162 real time: 1165 gc time: 156
10,000 fvars; let-syntax implementation
cpu time: 10928 real time: 10964 gc time: 1374
1,000 fvars; let implementation
cpu time: 20 real time: 20 gc time: 3
10,000 fvars; let implementation
cpu time: 225 real time: 225 gc time: 54
|#
(begin-for-syntax
(define current-fvars (make-parameter 10000))
(define (bind-fvars s n tail)
#`(let-syntax
#,(for/list ([i (in-range 0 n)])
(with-syntax ([fvar (syntax-local-introduce (format-id #f "fv~a"
i))]
[offset i]
[stack s])
#`[fvar (make-set!-transformer
(lambda (stx)
(syntax-case stx ()
[(set! id v)
#`(vector-set! stack offset v)]
[id (identifier? #'id)
#`(vector-ref stack offset)])))]))
#,tail)))
(define-syntax (my-module stx)
(syntax-case stx ()
[(_ e ...)
(with-syntax ([s #'stack])
#`(let ([s (make-vector #,(current-fvars) (void))])
#,(bind-fvars #'s (current-fvars) #`(begin e ...))))]))
(define-namespace-anchor a)
(displayln "1,000 fvars; let-syntax implementation")
(time
(eval
'(begin
(begin-for-syntax
(current-fvars 1000))
(my-module
(set! fv0 8)
(set! fv1 8)
(+ fv0 fv1)))
(namespace-anchor->namespace a)))
(displayln "10,000 fvars; let-syntax implementation")
(time
(eval
'(begin
(begin-for-syntax
(current-fvars 10000))
(my-module
(set! fv0 8)
(set! fv1 8)
(+ fv0 fv1)))
(namespace-anchor->namespace a)))
(define-for-syntax (bind-fvars2 n tail)
#`(let #,(for/list ([i (in-range 0 n)])
(with-syntax ([fvar (syntax-local-introduce (format-id #f "fv~a"
i))])
#`[fvar (void)]))
#,tail))
(define-syntax (my-module2 stx)
(syntax-case stx ()
[(_ e ...)
(bind-fvars2 (current-fvars) #`(begin e ...))]))
;; expansion time increases with the number of let bindings, but not nearly as
bad
;; expansion time seems to be 1ms per fvar, i.e., per let-syntax?
(displayln "1,000 fvars; let implementation")
(time
(eval
'(begin
(begin-for-syntax
(current-fvars 1000))
(my-module2
(set! fv0 8)
(set! fv1 8)
(+ fv0 fv1)))
(namespace-anchor->namespace a)))
(displayln "10,000 fvars; let implementation")
(time
(eval
'(begin
(begin-for-syntax
(current-fvars 10000))
(my-module2
(set! fv0 8)
(set! fv1 8)
(+ fv0 fv1)))
(namespace-anchor->namespace a)))