[racket] improving speed of namespace attachment/requiring?

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Wed Feb 27 23:12:58 EST 2013

At Wed, 27 Feb 2013 16:05:25 -0700, Danny Yoo wrote:
> Hi everyone,
> 
> I'm working on making Whalesong be the evaluator for the WeScheme
> environment.  One problem I'm running into is the speed of REPL
> compilation.  I'm compiling on the server side, and I want the
> compiler to be stateless, so I create a clean namespace per individual
> interaction.
> 
> I'm doing something like this to create such namespaces:
> 
> ###################################
> #lang racket/base
> (define this-namespace (make-base-empty-namespace))
> (define (make-repl-namespace [module-path 'racket/base])
>   (parameterize ([current-namespace this-namespace])
>     (dynamic-require module-path #f))
>   (make-empty-namespace)
>   (define ns (make-empty-namespace))
>   (parameterize ([current-namespace ns])
>     (namespace-attach-module this-namespace module-path)
>     (namespace-require module-path))
>   ns)
> ###################################
> 
> 
> I create a single this-namespace to hold the instantiation of my base
> language module, and then inject it into a fresh namespace I construct
> per repl interaction.  That way, I instantiate my language module just
> once, rather than per interaction.  I note, though, that if I do this,
> the cost of attaching and requiring the module can still be a little
> expensive, depending on the language namespace I want to construct.
> 
> 
> For example, if I execute the following:
> 
> ###################################
> (for ([i 10])
>   (time (parameterize ([current-namespace (make-repl-namespace
> 'lang/htdp-beginner)])
>           (let ()
>             (compile
>              '(* x 3))))))
> ###################################
> 
> then I observe the following on the Racket console:
> 
> ######################
> cpu time: 256 real time: 275 gc time: 69
> cpu time: 22 real time: 23 gc time: 0
> cpu time: 63 real time: 65 gc time: 34
> cpu time: 21 real time: 22 gc time: 0
> cpu time: 20 real time: 20 gc time: 0
> cpu time: 38 real time: 41 gc time: 11
> cpu time: 23 real time: 23 gc time: 0
> cpu time: 43 real time: 44 gc time: 21
> cpu time: 24 real time: 26 gc time: 0
> cpu time: 17 real time: 17 gc time: 0
> ######################
> 
> This isn't so bad, on my laptop, but I expect the times to double at
> least when I deploy to my slower Amazon EC2 servers.  Can I do better
> here? If I switch the language here to 'racket/base, then it's a lot
> faster: why?
> 
> 
> I still have some tricks I can play (like memoizing the compiler).
> But anything I can do to keep the compilation cost down would be
> greatly appreciated.  Thanks!

I think you want to generate new namespaces that share the module
registry, which means that they share the visit of the module indicated
by `module-path'.

Below is one way to do it, although probably there should be an easier
way.

----------------------------------------

#lang racket/base

(define make-fresh-namespace (eval 
                              '(lambda () 
                                 (variable-reference->empty-namespace
                                  (#%variable-reference)))
                              (make-base-namespace)))

(define (make-repl-namespace [module-path 'racket/base])
  (define ns (make-fresh-namespace))
  (parameterize ([current-namespace ns])
    (namespace-require module-path))
  ns)

(for ([i 10])
  (time (parameterize ([current-namespace 
                        (make-repl-namespace 'lang/htdp-beginner)])
          (let ()
            (compile
             '(* x 3))))))




Posted on the users mailing list.