Translation. Partial evaluation in guile [jsr-0015]
Translation. Partial evaluation in guile [jsr-0015]
tips: Click on the translation to expand and view the original text. 点击译文可展开查看原文。
Translated according to the version last updated on 11 October 2011 10:01 AM.
根据2011年10月11日上午10:01更新的版本翻译。
朋友们,就在刚刚发生了一件很棒的事情:Guile 获得了一个值得尊敬的
内联器
。
Friends, something awesome just happened: Guile just got itself a respectable inliner.
我以前在这个博客说过,引用评论者 Rémi Forax 的话说,“内联是所有优化之母”。的确如此,内联为代码移动,常量折叠,死代码消除,循环优化开辟了空间。然而,功劳可能更应该归功于
部分求值
,这位所有内联优化之母。
I have said before on this blog, quoting commenter Rémi Forax, that "inlining is the mother of all optimizations". It is true that inlining opens up space for code motion, constant folding, dead code elimination, and loop optimizations. However, credit might be better laid at the feet of partial evaluation, the mother of all inlining algorithms.
部分求值是一种源到源的转换,它接收你的程序并产生一个更好的程序:其中任何可以在编译时完成的计算都已完成,只留下那些需要在运行时进行的计算。
Partial evaluation is a source-to-source transformation that takes your program and produces a better one: one in which any computation that can be done at compile-time is already made, leaving only those computations that need to happen at run-time.
例如,这个应用
For example, the application
(+ 2 3)
可以明确地在编译时求值。我们说源表达式
(+ 2 3)
通过常数折叠简化为 5
,得到的结果(在这个情况下是 5
)是
留存
表达式。
can clearly be evaluated at compile-time. We say that the source expression (+ 2 3) reduces to 5 via constant folding. The result, 5 in this case, is the residual expression.
一个更复杂的例子:
A more complicated example would look like:
(let ((string->chars
(lambda (s)
(define char-at
(lambda (n) (string-ref s n)))
(define len
(lambda () (string-length s)))
(let loop ((i 0))
(if (< i (len))
(cons (char-at i)
(loop (1+ i)))
'())))))
(string->chars "yo"))
=> (list #\y #\o)
当我在这里写下
=>
,你应该将它读作“
在编译时留存
”。在这里,我们的输入程序在编译时留存为一个简单的 list 构造。循环完全被
展开
,字符串引用折叠,所有
叶子过程
都被内联。
Here when I write =>, you should read it as, "residualizes at compile-time to". In this case our input program residualized, at compile-time, to a simple list construction. The loop was totally unrolled, the string-refs folded, and all leaf procedures were inlined.
很棒吧?
Neat, eh?
我似乎离题了,不好意思!
It seems I have digressed. Sorry about that!
我谈到了闭包和词法作用域,这是可以实现静态内联的 JS 语言属性。JavaScript 实现支持内联的第二种(同时更重要的)方式是动态内联。几个月前我用这个问题
钓鱼
。当动态内联工作时,它非常棒,尽管有一些条件限制了启发式方式(向下滚动到“inlining”,注意在这几个月中具体的启发式集合已经变化了。)。
I spoke about closures and lexical scope, properties of the JS language that can enable static inlining. The second (and more important) way that JS implementations can support inlining is dynamically. I trolled about that some months ago. Dynamic inlining is fantastic, when it works, though there are limiting heuristics (scroll down to "inlining", and note that the exact set of heuristics have changed in the intervening months).
所以我的最后一点是关于 Guile 表现出色,而 JS 实现表现不佳的事,出于公平起见,我们现在谈一个相反的点。我希望能够实现动态内联,但这意味着要将中间表示与 scheme 函数关联起来。由于 Guile 能够
提前编译
代码,这意味着我们将不得不将中间表示序列化到磁盘上,就像 GCC 的新
链接时优化器
(LTO)做的那样。但我希望这个实现推迟到之后我们将编译后 Guile 的格式更改为 ELF,否则我们会有运行时内存大小膨胀的风险。
So my last point was about something that Guile does well that JS implementations do poorly, and it's fair that this point should be the reverse. I would like to be able to dynamically inline, but this would mean associating the intermediate representation with Scheme functions. As Guile can compile code ahead-of-time, this means we would have to serialize the IR out to disk, in much the same way as GCC's new link-time optimizer (LTO) does. But I would like to put that off until we change the format of compiled Guile code to be ELF. Otherwise we run the risk of bloating our runtime memory size.