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字节。
有什么区别?
埃德
这是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)))))))