简体   繁体   English

需要调用/按名称调用Lisp解释器策略的开销

[英]Overhead of call-by-need / call-by-name Lisp interpreter strategy

I've a partially finished interpreter for a lexically scoped 'pure Lisp' (no set! ) that uses a call-by-need evaluation model which comes down to call-by-name with simple caching, the interpreter naturally uses an environment-based evaluation model. 我有一个部分完成的解释器,用于词法范围的“纯Lisp”(没有set! ),它使用按需调用评估模型,该模型通过简单的缓存进行逐个调用,解释器自然使用环境 -基于评估模型。

The standard way of evaluating lambda abstractions, as in, constructing a new environment from the formal paramatres and the environment in which the abstraction is evaluated and simply placing the evaluations of the arguments in their own environment in it. 评估lambda抽象的标准方法,例如,从正式的paramatres和评估抽象的环境构建一个新环境,并简单地将参数的评估放在它们自己的环境中。 Then evaluate the body of the abstraction in the new environment wouldn't work because it would mean a call-by-value semantics. 然后在新环境中评估抽象主体是行不通的,因为它意味着按值调用语义。

My solution to this problem was replacing where required the idea of 'environments' with 'lookup functions', which just take a symbol as argument, and produce an associated datum. 我对这个问题的解决方案是用“查找函数”替换需要“环境”的概念,它只是将符号作为参数,并生成相关的数据。 Which can easily be made from an environment. 这可以很容易地从一个环境。 Lambda-applications are just done by evaluating the body again with a lookup function which is made from both the environment in which the definition lies, and in which the argument lie. Lambda应用程序只需通过查找函数再次对主体进行评估,该查找函数由定义所在的环境和参数所在的环境构成。 Which evaluates them lazily and only when required. 它只是在需要时懒洋洋地评估它们。

What I wonder though is what the overhead from this model is, how expensive is it to generate these lookups for every application, the code for these lookups is pretty large. 我想知道的是这个模型的开销是多少,为每个应用程序生成这些查找的成本是多少,这些查找的代码非常大。 I know that lambda application and creation in Scheme is fairly cheap and many sources advocate using them extensively to maintain the readability of code even though in a lot of cases they would have a slight overhead. 我知道在Scheme中的lambda应用程序和创建是相当便宜的,许多来源主张广泛使用它们来维护代码的可读性,即使在很多情况下它们会有轻微的开销。 But as lambda-applications are ubiquitous in any lisp, I wonder just how much performance could be saved from using a potentially different model. 但由于lambda应用程序在任何lisp中无处不在,我想知道使用可能不同的模型可以节省多少性能。 I tried searching this on google but all the models for call-by-need interpreters I found were even more awkward, but often so to accommodate for set! 我尝试在谷歌搜索这个,但我找到的所有需要​​解释的解释器的模型更加尴尬,但通常是为了适应set! .

Some relevant pieces of my code: 我的代码的一些相关部分:

The evaluator that uses the lookup function: 使用查找功能的求值程序:

; evaluates a datum using a lookup
; lookup is a function which takes a symbol as an argument and produces the value
; some sorts of more powerful environment
; if lookup is a standard environment, treats it as such
(define (eval-datum! d lookup)
  (cond 
    ((quoted? d) (dequote d)) ; if quoted, just dequote
    ((hastype d symbol-type) ; symbols ... 
     (if (procedure? lookup) ; checks if it's an environment or a lookup function
         (lookup d)
         (lookup-symbol d lookup)))
    ((hastype d pair-type) (eval-pair! d lookup)) ; function application
    (else d))) ; rest is considered self-evaluating

And the function that generates the more advanced lookups. 以及生成更高级查找的函数。 This especially is what worries me, though it's tail-recursive and uses very cheap eq? 这特别令我担心,虽然它是尾递归并且使用非常便宜的eq? and null? null? comparisons, it doesn't look as efficient as simply using assq on an environment list, and some times even the lack of random access on those worries me a bit. 比较,它看起来assq在环境列表上简单地使用assq那样有效,有时甚至缺乏对这些的随机访问让我有点担心。

; extends a lookup for use in a lambda abstraction
; the inner environment is where the lambda is defined
; the outer environment where it is applied
; params can be either a pair or a symbol
; params automatically tries to match the argument's pattern
(define (extend-lookup! params args inner-env outer-env)
  (lambda (s)
    (let checkparams ((params params) (args args))
      (cond
        ((eq? s params) (datum args)) ; for an improper list or a single symbol, simply turn the arglist into an evaluable list
        ((null? params) (lookup-symbol s inner-env)) ; if the number of paramatres are exhausted, simply use the inner-env
        ((eq? s (car params)) ; in case of a formal parametre match ...
              (refeval! args 0 outer-env)) ; evaluate the needed argument and return it
        (else (checkparams (cdr params) (cdr args))))))) ; repeat for the next paramatre

It should be apparent that the evaluator works by a simple term-reduction system that when evaluating expressions in a list simply replaces them with their result, quoting them whenever the result is not considered self-evaluating. 显而易见的是,评估者通过一个简单的术语缩减系统来工作,当评估列表中的表达式时,只需用结果替换它们,只要结果不被认为是自我评估就引用它们。 Which is possible because it's a purely functional lisp. 这是可能的,因为它是一个纯功能的lisp。 It also captures caching and most of the garbage collection in one go. 它还可以一次性捕获缓存和大部分垃圾回收。

I should add that one of the things I always had very poor conception of is overhead and complexity theory. 我应该补充一点,我总是有一个非常糟糕的概念是开销和复杂性理论。 For those that say 'If you want performance, why are you making your interpreter in Lisp?', it's only a test of the general structure to see if it works, I'll be re-writing it in C-- soon enough. 对于那些说“如果你想要表现,你为什么要用Lisp制作翻译?”的人来说,它只是对一般结构的考验,看看它是否有效,我将很快用C--重写它。

Ah, I couldn't even submit this at first because the tag 'call-by-need' doesn't already exist, promising start. 啊,我最初甚至无法提交这个,因为标签“按需调用”尚不存在,很有希望开始。

Another thing that 'you' might do is simply mark every single datum with an environment on its own that can be retrieved from it in the data structure, it may seem exhaustive but in the end all that needs to be marked come to think of it are lists and a very special case that the body of the lambda abstraction contains only one symbol. “你可能做的另一件事就是简单地用每个单独的数据标记一个可以在数据结构中从中检索的环境,它可能看起来很详尽,但最后所有需要标记的都会想到它是列表和一个非常特殊的情况,lambda抽象的主体只包含一个符号。 For other data in 'your' model its evaluation is irrespective from its environment. 对于“您的”模型中的其他数据,其评估与其环境无关。

This also solves a major problem with lazy cons cells and lists that are just passed around to other places in the programme. 这也解决了懒惰的cons细胞和列表的一个主要问题,它们只是传递给程序中的其他位置。

So assuming a list itself is marked with an environment, you can just extract the environment whenever you evaluate it. 因此,假设列表本身标有环境,您可以在评估时提取环境。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM