简体   繁体   English

如何实现延续?

[英]How to implement continuations?

I'm working on a Scheme interpreter written in C.我正在研究用 C 编写的 Scheme 解释器。 Currently it uses the C runtime stack as its own stack, which is presenting a minor problem with implementing continuations.目前它使用 C 运行时堆栈作为自己的堆栈,这在实现延续方面存在一个小问题。 My current solution is manual copying of the C stack to the heap then copying it back when needed.我当前的解决方案是将 C 堆栈手动复制到堆中,然后在需要时将其复制回来。 Aside from not being standard C, this solution is hardly ideal.除了不是标准的 C 之外,这种解决方案也不是很理想。

What is the simplest way to implement continuations for Scheme in C?在 C 中实现 Scheme 延续的最简单方法是什么?

A good summary is available in Implementation Strategies for First-Class Continuations , an article by Clinger, Hartheimer, and Ost. Clinger、Hartheimer 和 Ost 的文章《 Implementation Strategies for First-Class Continuations 》中有一个很好的总结。 I recommend looking at Chez Scheme's implementation in particular.我建议特别查看 Chez Scheme 的实施。

Stack copying isn't that complex and there are a number of well-understood techniques available to improve performance.堆栈复制并不那么复杂,并且有许多易于理解的技术可用于提高性能。 Using heap-allocated frames is also fairly simple, but you make a tradeoff of creating overhead for "normal" situation where you aren't using explicit continuations.使用堆分配的帧也相当简单,但是您需要权衡为不使用显式延续的“正常”情况创建开销。

If you convert input code to continuation passing style (CPS) then you can get away with eliminating the stack altogether.如果您将输入代码转换为持续传递样式 (CPS),那么您可以完全消除堆栈。 However, while CPS is elegant it adds another processing step in the front end and requires additional optimization to overcome certain performance implications.然而,虽然 CPS 很优雅,但它在前端增加了另一个处理步骤,并且需要额外的优化来克服某些性能影响。

I remember reading an article that may be of help to you: Cheney on the MTA :-)我记得读过一篇可能对你有帮助的文章: MTA 上的切尼:-)

Some implementations of Scheme I know of, such as SISC , allocate their call frames on the heap.我知道的一些 Scheme 实现,例如SISC ,在堆上分配它们的调用帧。

@ollie: You don't need to do the hoisting if all your call frames are on the heap. @ollie:如果所有调用帧都在堆上,则不需要进行提升。 There's a tradeoff in performance, of course: the time to hoist, versus the overhead required to allocate all frames on the heap.当然,性能需要权衡:提升时间与分配堆上所有帧所需的开销。 Maybe it should be a tunable runtime parameter in the interpreter.也许它应该是解释器中一个可调的运行时参数。 :-P :-P

If you are starting from scratch, you really should look in to Continuation Passing Style (CPS) transformation.如果您从头开始,您真的应该考虑继续传递样式 (CPS) 转换。

Good sources include "LISP in small pieces" and Marc Feeley's Scheme in 90 minutes presentation .好的资源包括“LISP in smallpieces”和Marc Feeley 的 90 分钟演示方案

It seems Dybvig's thesis is unmentioned so far.到目前为止,似乎没有提到 Dybvig 的论文。 It is a delight to read.阅读是一种享受。 The heap based model is the easiest to implement, but the stack based is more efficient.基于堆的 model 最容易实现,但基于堆栈的效率更高。 Ignore the string based model.忽略基于字符串的 model。

R. R。 Kent Dybvig.肯特·戴维格。 "Three Implementation Models for Scheme". “方案的三种实施模式”。 http://www.cs.indiana.edu/~dyb/papers/3imp.pdf http://www.cs.indiana.edu/~dyb/papers/3imp.pdf

Also check out the implementation papers on ReadScheme.org.另请查看 ReadScheme.org 上的实施文件。 https://web.archive.org/http://library.readscheme.org/page8.html https://web.archive.org/http://library.readscheme.org/page8.html

The abstract is as follows:摘要如下:

This dissertation presents three implementation models for the Scheme Programming Language.本文介绍了Scheme编程语言的三种实现模型。 The first is a heap-based model used in some form in most Scheme implementations to date;第一个是基于堆的 model 在迄今为止的大多数 Scheme 实现中以某种形式使用; the second is a new stack-based model that is considerably more efficient than the heap-based model at executing most programs;第二个是新的基于堆栈的 model,它在执行大多数程序时比基于堆的 model 效率更高; and the third is a new string-based model intended for use in a multiple-processor implementation of Scheme.第三个是新的基于字符串的 model,旨在用于 Scheme 的多处理器实现。

The heap-based model allocates several important data structures in a heap, including actual parameter lists, binding environments, and call frames.基于堆的 model 在堆中分配了几个重要的数据结构,包括实际参数列表、绑定环境和调用帧。

The stack-based model allocates these same structures on a stack whenever possible.基于堆栈的 model 尽可能在堆栈上分配这些相同的结构。 This results in less heap allocation, fewer memory references, shorter instruction sequences, less garbage collection, and more efficient use of memory.这导致更少的堆分配、更少的 memory 引用、更短的指令序列、更少的垃圾收集以及更有效地使用 memory。

The string-based model allocates versions of these structures right in the program text, which is represented as a string of symbols.基于字符串的 model 在程序文本中分配这些结构的版本,以符号字符串表示。 In the string-based model, Scheme programs are translated into an FFP language designed specifically to support Scheme.在基于字符串的 model 中,Scheme 程序被翻译成专门为支持 Scheme 而设计的 FFP 语言。 Programs in this language are directly executed by the FFP machine, a multiple-processor string-reduction computer.这种语言的程序直接由 FFP 机器执行,FFP 机器是一种多处理器字符串缩减计算机。

The stack-based model is of immediate practical benefit;基于堆栈的 model 具有直接的实际优势; it is the model used by the author's Chez Scheme system, a high-performance implementation of Scheme.就是作者 Chez Scheme 系统使用的 model,Scheme 的高性能实现。 The string-based model will be useful for providing Scheme as a high-level alternative to FFP on the FFP machine once the machine is realized.一旦实现机器,基于字符串的 model 将有助于在 FFP 机器上提供 Scheme 作为 FFP 的高级替代方案。

Besides the nice answers you've got so far, I recommend Andrew Appel's Compiling with Continuations .除了到目前为止你得到的很好的答案,我推荐 Andrew Appel 的Compiling with Continuations It's very well written and while not dealing directly with C, it is a source of really nice ideas for compiler writers.它写得非常好,虽然不直接处理 C,但它是编译器编写者非常好的想法的来源。

The Chicken Wiki also has pages that you'll find very interesting, such as internal structure and compilation process (where CPS is explained with an actual example of compilation). Chicken Wiki 还有一些您会发现非常有趣的页面,例如内部结构编译过程(其中 CPS 以编译的实际示例进行解释)。

Examples that you can look at are: Chicken (a Scheme implementation, written in C that support continuations);您可以查看的示例有: Chicken (一个 Scheme 实现,用 C 编写,支持延续); Paul Graham's On Lisp - where he creates a CPS transformer to implement a subset of continuations in Common Lisp; Paul Graham 的On Lisp - 他创建了一个 CPS 转换器来实现 Common Lisp 中的延续子集; and Weblocks - a continuation based web framework, which also implements a limited form of continuations in Common Lisp.Weblocks - 基于延续的 web 框架,它还在 Common Lisp 中实现了有限形式的延续。

Continuations aren't the problem: you can implement those with regular higher-order functions using CPS.延续不是问题:您可以使用 CPS 实现具有常规高阶函数的那些。 The issue with naive stack allocation is that tail calls are never optimised, which means you can't be scheme.天真的堆栈分配的问题是尾调用永远不会优化,这意味着你不能计划。

The best current approach to mapping scheme's spaghetti stack onto the stack is using trampolines: essentially extra infrastructure to handle non-C-like calls and exits from procedures.当前将方案的意大利面条堆栈映射到堆栈上的最佳方法是使用蹦床:本质上是额外的基础设施来处理非 C 类调用和过程退出。 See Trampolined Style (ps) .参见蹦床风格 (ps)

There's some code illustrating both of these ideas.一些代码说明了这两种想法。

The traditional way is to use setjmp and longjmp , though there are caveats.传统方法是使用setjmplongjmp ,尽管有一些警告。

Here's a reasonably good explanation这是一个相当不错的解释

Continuations basically consist of the saved state of the stack and CPU registers at the point of context switches.延续基本上包括在上下文切换点保存的堆栈和 CPU 寄存器的 state。 At the very least you don't have to copy the entire stack to the heap when switching, you could only redirect the stack pointer.至少你不必在切换时将整个堆栈复制到堆中,你可以只重定向堆栈指针。

Continuations are trivially implemented using fibers.使用纤维很容易实现延续。 http://en.wikipedia.org/wiki/Fiber_%28computer_science%29 . http://en.wikipedia.org/wiki/Fiber_%28computer_science%29 The only things that need careful encapsulation are parameter passing and return values.唯一需要仔细封装的是参数传递和返回值。

In Windows fibers are done using the CreateFiber/SwitchToFiber family of calls.在 Windows 中,光纤是使用 CreateFiber/SwitchToFiber 系列调用完成的。 in Posix-compliant systems it can be done with makecontext/swapcontext.在符合 Posix 的系统中,可以使用 makecontext/swapcontext 来完成。

boost::coroutine has a working implementation of coroutines for C++ that can serve as a reference point for implementation. boost::coroutine 有一个用于 C++ 的协同程序的工作实现,可以作为实现的参考点。

As soegaard pointed out, the main reference remains R. Kent Dybvig. "Three Implementation Models for Scheme"正如soegaard指出的,主要参考仍然是R. Kent Dybvig. "Three Implementation Models for Scheme" R. Kent Dybvig. "Three Implementation Models for Scheme" R. Kent Dybvig. "Three Implementation Models for Scheme" . R. Kent Dybvig. "Three Implementation Models for Scheme"

The idea is, a continuation is a closure that keeps its evaluation control stack.这个想法是,延续是保持其评估控制堆栈的闭包。 The control stack is required in order to continue the evalution from the moment the continuation was created using call/cc .从使用call/cc创建延续的那一刻起,需要控制堆栈才能继续评估。

Oftenly invoking the continuation makes long time of execution and fills the memory with duplicated stacks.经常调用延续会导致执行时间很长,并用重复的堆栈填充 memory。 I wrote this stupid code to prove that, in mit-scheme it makes the scheme crash,我写了这个愚蠢的代码来证明,在 mit-scheme 中它会使方案崩溃,

The code sums the first 1000 numbers 1+2+3+...+1000 .该代码将前 1000 个数字1+2+3+...+1000相加。

(call-with-current-continuation 
 (lambda (break)
   ((lambda (s) (s s 1000 break))
    (lambda (s n cc)
      (if (= 0 n)
          (cc 0)
          (+ n
             ;; non-tail-recursive,
             ;; the stack grows at each recursive call
             (call-with-current-continuation
              (lambda (__)
                (s s (- n 1) __)))))))))

If you switch from 1000 to 100 000 the code will spend 2 seconds, and if you grow the input number it will crash.如果您从 1000 切换到 100 000,代码将花费 2 秒,如果您增加输入数字,它将崩溃。

Use an explicit stack instead.请改用显式堆栈。

Patrick is correct, the only way you can really do this is to use an explicit stack in your interpreter, and hoist the appropriate segment of stack into the heap when you need to convert to a continuation.帕特里克是正确的,你真正能做到这一点的唯一方法是在你的解释器中使用一个显式的堆栈,当你需要转换为一个延续时,将适当的堆栈段提升到堆中。

This is basically the same as what is needed to support closures in languages that support them (closures and continuations being somewhat related).这与在支持闭包的语言中支持闭包所需的基本相同(闭包和延续有些相关)。

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

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