[racket] Why parameterize is so sloooow?
At Fri, 16 Aug 2013 13:59:55 +0400, Roman Klochkov wrote:
>
> I compared parameterize with lexical var
> ----
> > (require rackunit)
> > (define my-parameter (make-parameter (box 0)))
> > (time
> (parameterize ([my-parameter (box 0)])
> (for ([x (in-range 10000000)])
> (set-box! (my-parameter)
> (add1 (unbox (my-parameter)))))
> (check-equal? (unbox (my-parameter)) 10000000)))
> cpu time: 3578 real time: 3610 gc time: 0
> > (time
> (let ([my-parameter (box 0)])
> (for ([x (in-range 10000000)])
> (set-box! my-parameter
> (add1 (unbox my-parameter))))
> (check-equal? (unbox my-parameter) 10000000)))
> cpu time: 47 real time: 47 gc time: 0
> ----
>
> 100 times difference!
>
> The same experiment with Common Lisp (SBCL):
> ----
> CL-USER> (setf *a* (list 0))
> (0)
> CL-USER> (time (progn (loop :for i :from 0 :below 10000000
> :do (setf (car *a*) (+ 1 (car *a*)))) (= (car *a*) 10000000)))
> Evaluation took:
> 0.063 seconds of real time
> 0.062500 seconds of total run time (0.062500 user, 0.000000 system)
> 98.41% CPU
> 172,464,541 processor cycles
> 0 bytes consed
>
> T
> CL-USER> (let ((a (list 0))) (time (loop :for i :from 0 :below 10000000
> :do (setf (car a) (+ 1 (car a))))) (= (car a) 10000000))
>
> Evaluation took:
> 0.047 seconds of real time
> 0.046875 seconds of total run time (0.046875 user, 0.000000 system)
> 100.00% CPU
> 132,098,942 processor cycles
> 0 bytes consed
>
> T
> ----
> Only 1.5 times.
>
> Is it undesirable to use parameterize as replacement for common lisp special
> variables? What is it designed for then?
Parameters in Racket are grouped together in a an extra layer called a
"parameterization", which enables capture of the current values of all
parameters. For example, when a new thread is created in Racket, then
the new inherits all of the current parameter values from the creating
thread. A lack of cleverness in that layer is probably the main effect
on performance in yuor example.
Using a raw, symbol-keyed continuation mark would be closer to a Common
Lisp special variable, I think. On my machine:
;; parameter
> (time
(parameterize ([my-parameter (box 0)])
(for ([x (in-range 10000000)])
(set-box! (my-parameter)
(add1 (unbox (my-parameter)))))
(check-equal? (unbox (my-parameter)) 10000000)))
cpu time: 2539 real time: 2537 gc time: 0
;; direct
> (time
(let ([my-parameter (box 0)])
(for ([x (in-range 10000000)])
(set-box! my-parameter
(add1 (unbox my-parameter))))
(check-equal? (unbox my-parameter) 10000000)))
cpu time: 45 real time: 45 gc time: 0
;; raw continuation mark:
> (time
(let ([my-parameter
(lambda ()
(continuation-mark-set-first #f 'my-parameter))])
(with-continuation-mark
'my-parameter
(box 0)
(begin
(for ([x (in-range 10000000)])
(set-box! (my-parameter)
(add1 (unbox (my-parameter)))))
(check-equal? (unbox (my-parameter)) 10000000)))))
cpu time: 244 real time: 243 gc time: 0
That's still a fact of 5 difference. I expect that dynamic binding and
special variables have played a more prominent role in Common Lisp than
parameters or even continuation-mark lookup in Racket, and so it would
make sense that more work has been done in the SBCL to make them fast.