The purpose of the Chez scheme: --program vs - script

Consider this bit of Chez code :

  (import (chezscheme))

 (define (list-enumerate ls val proc)
   (let loop ((ls ls) (return? #f) (val val))
     (if (or (null? ls)
             return?)
         val
         (call-with-values ​​(lambda () (proc val (car ls)))
           (lambda (return? val)
             (loop (cdr ls) return? val))))))

 (define (list-index ls proc)
   (list-enumerate ls
                   0
                   (lambda (i elt)
                     (if (proc elt)
                         (values ​​#ti)
                         (values ​​#f (+ i 1))))))

 (define n 100000)

 (define data (iota n))

 (time (list-index data (lambda (elt) (= elt (- n 1)))))

Run it:

  ~ $ scheme --script ~ / scratch / _list-enumerate-allocation-test-chez-a.sps 
 (time (list-index data ...))
     no collections
     3 ms elapsed cpu time
     4 ms elapsed real time
     8 bytes allocated

Wow, he reports that only 8 bytes were allocated.

Run it again using the --program instead of --script :

  ~ $ scheme --program ~ / scratch / _list-enumerate-allocation-test-chez-a.sps 
 (time (list-index data ...))
     no collections
     3 ms elapsed cpu time
     3 ms elapsed real time
     800,000 bytes allocated

Yikes, allocated 800,000 bytes.

What's the difference?

Ed

+4
source share
1 answer

Here's a note from Kent Dybwig in response:


This is an interesting question.

When launched with a - script that uses REPL semantics, the variables defined in the script, such as list-enumerate and list-index, are mutable, which inhibits cross-procedure optimization, including embedding. when run with -program, however the variables are immutable, which allows interprocedural optimizations.

In this case, --program allows the compiler to include in the list-enumerate list in the list-index body and, in turn, the lambda expression in the list-index index, the body to list — to list the body. The end result is a conditional expression in the expression of the manufacturer. This causes the compiler to create a closure for the consumer to avoid code duplication along conditional branches and so on. This closure is created each time through a list-enumerate cycle, resulting in additional distribution overhead. How often optimization happens. Mostly you win, but sometimes you lose. The good news is, in general, the benefits of the weight it costs, even in your program. I called list-index in a loop (modified code below) and found that with --program, the code runs about 30% faster.

Kent


  (import (chezscheme))

 (define (list-enumerate ls val proc)
   (let loop ((ls ls) (return? #f) (val val))
     (if (or (null? ls)
             return?)
         val
         (call-with-values ​​(lambda () (proc val (car ls)))
           (lambda (return? val)
             (loop (cdr ls) return? val))))))

 (define (list-index ls proc)
   (list-enumerate ls
                   0
                   (lambda (i elt)
                     (if (proc elt)
                         (values ​​#ti)
                         (values ​​#f (+ i 1))))))

 (define n 100000)

 (define data (time (iota n)))

 (let ()
 (define runalot
   (lambda (i thunk)
     (let loop ([ii])
       (let ([x (thunk)])
         (if (fx = i 1)
             x
             (loop (fx- i 1)))))))

 (time
   (runalot 1000
     (lambda ()
       (list-index data (lambda (elt) (= elt (- n 1))))))))
+4
source

Source: https://habr.com/ru/post/1303107/


All Articles