Chez Scheme分配:--program vs --script

dha*_*ech 4 scheme chez-scheme

考虑一下Chez Scheme代码的这一点:

(进口(chezscheme))

(定义(list-enumerate ls val proc)
  (让循环((ls ls)(返回?#f)(val val))
    (如果(或(空?ls)
            返回?)
        值
        (值调用(lambda()(proc val(car ls)))
          (lambda(返回值)
            (循环(cdr ls)return?val))))))

(定义(list-index ls proc)
  (列表枚举ls
                  0
                  (lambda(i elt)
                    (如果(proc elt)
                        (值#ti)
                        (值#f(+ i 1))))))

(定义n 100000)

(定义数据(iota n))

(时间(列表索引数据(lambda(elt)(= elt(-n 1)))))

运行:

〜$ scheme-脚本〜/ scratch / _list-enumerate-allocation-test-chez-a.sps 
(时间(列表索引数据...))
    没有收藏
    经过3 ms的cpu时间
    实时经过4毫秒
    分配了8个字节

哇,它报告只分配了8个字节。

让我们使用--program选项而不是再次运行它--script

〜$ scheme-程序〜/ scratch / _list-enumerate-allocation-test-chez-a.sps 
(时间(列表索引数据...))
    没有收藏
    经过3 ms的cpu时间
    实时经过3毫秒
    分配了800000字节

Yikes,分配了800000字节。

有什么区别?

埃德

dha*_*ech 5

这是Kent Dybvig的回应:


这是一个有趣的问题。

与使用REPL语义的--script一起运行时,脚本中定义的变量(例如list-enumerate和list-index)是可变的,这会妨碍过程内的优化,包括内联。但是,当使用--program运行时,变量是不可变的,从而允许进行过程间优化。

在这种情况下,--program允许编译器将list-enumerate内联到list-index的主体中,然后将list-index主体中的lambda表达式内联到list-enumerate的主体中。最终结果是带有值的调用生产者表达式中的条件表达式。这将导致编译器为使用者创建一个闭包,以避免在条件语句的then和else分支上重复代码。每次通过list-enumerate的循环都会创建此闭包,从而导致额外的分配开销。这就是优化经常采用的方式。通常,您赢了,但有时却输了。总的来说,好消息是,即使在您的程序中,收益也超过了他所付出的代价。我将对list-index的调用放在一个循环中(修改后的代码在下面),发现使用--program,该代码的运行速度提高了约30%。

肯特


(进口(chezscheme))

(定义(list-enumerate ls val proc)
  (让循环((ls ls)(返回?#f)(val val))
    (如果(或(空?ls)
            返回?)
        值
        (值调用(lambda()(proc val(car ls)))
          (lambda(返回值)
            (循环(cdr ls)return?val))))))

(定义(list-index ls proc)
  (列表枚举ls
                  0
                  (lambda(i elt)
                    (如果(proc elt)
                        (值#ti)
                        (值#f(+ i 1))))))

(定义n 100000)

(定义数据(时间(iota n)))

(让()
(定义Runalot
  (lambda(我重击)
    (让循环([ii])
      (让[[x(thunk)])
        (如果(fx = i 1)
            X
            (循环(fx- i 1))))))))

(时间
  (runalot 1000
    (lambda()
      (列表索引数据(lambda(elt)(= elt(-n 1)))))))