λ-演算:在函数的内存表示中最有效的是什么?


9

我想比较函数编码(Church / Scott)与传统编码(汇编器/ C)数据结构的性能。

但是在我这样做之前,我需要知道函数在内存中的表示效率。该功能当然可以部分应用(也称为闭包)。

我对当前流行的功能性语言(Haskell,ML)使用的编码算法以及可以实现的最有效的一种语言都感兴趣。


奖金点:有这样的编码该地图函数编码整数天然整数(shortint等,在C)。可能吗


我根据效率来评估效率。换句话说,编码越有效,对功能数据结构的计算性能的影响就越小。


我所有的Google尝试都失败了,也许我不知道正确的关键字。
福特O.17年

您可以编辑问题以阐明“有效”的含义吗?有效的是什么?当需要高效的数据结构时,您需要指定要对数据结构执行的操作,因为这会影响数据结构的选择。还是说编码尽可能节省空间?
DW

1
这是相当广泛的。有很多用于lambda演算的抽象机,旨在有效执行它(例如,参见SECD,CAM,Krivine's,STG)。最重要的是,您需要考虑Church / Scott编码的数据,这带来了更多的问题。例如,在教堂编码的列表中,尾部操作必须为O(n)而不是O(1)。我想我在某处读到,系统F中使用O(1)头尾操作的列表编码仍然是一个未解决的问题。

@DW我在谈论性能/开销。例如,在教堂列表和Haskell列表之间进行高效编码映射应该花费相同的时间。
福特O.17年

什么操作的性能?您想使用这些功能做什么?您想对这些功能进行一些评估吗?一次还是在多个值上评估相同的功能?与他们做其他事情吗?您是否只是在问如何编译功能(用功能语言编写)以便可以尽可能高效地执行?
DW

Answers:


11

问题是,在函数编码方面确实没有多少回旋余地。主要选项如下:

  • 术语重写:您将函数存储为它们的抽象语法树(或其某种编码。在调用函数时,您手动遍历语法树以用参数替换其参数。这很简单,但在时间和空间方面效率低下。

  • 闭包:您可以通过某种方式表示函数,例如语法树,更有可能表示机器代码。在这些函数中,您以某种方式通过引用来引用您的参数。它可以是指针偏移量,可以是整数或De Bruijn索引,也可以是名称。然后,您将函数表示为闭包:函数“指令”(树,代码等)与包含函数所有自由变量的数据结构配对。在实际应用函数时,它会以某种方式知道如何使用环境,指针算术等在其数据结构中查找自由变量。

我敢肯定还有其他选择,但这是基本的选择,我怀疑几乎所有其他选择都是基本闭包结构的变体或优化。

因此,就性能而言,闭包在总体上比术语重写要好。在这些变体中,哪个更好?那在很大程度上取决于您的语言和体系结构,但是我怀疑“带有包含免费变量的结构的机器代码”是最有效的。它具有函数需要的所有内容(指令和值),仅此而已,并且调用并不会最终进行长期遍历。

我对当前两种流行的功能语言(Haskell,ML)的编码算法都感兴趣

我不是专家,但我99%的大多数ML口味都使用了我描述的闭包的某些变体,尽管可能会进行一些优化。请参阅内容(可能已过时)。

由于懒惰的评估,Haskell所做的事情有些复杂:它使用了Spineless Tagless Graph Rewriting

也是最有效的方法

什么是最有效的?没有一种实现在所有输入上都是最高效的,因此您会获得平均而言效率很高的实现,但是每种实现在不同的场景下都非常出色。因此,没有确定最高效率或最低效率的排名。

这里没有魔术。要存储函数,您需要以某种方式存储其自由值,否则编码的信息少于函数本身的信息。也许您可以通过部分评估来优化一些自由值,但这会影响性能,因此您必须小心确保始终停止运行。

而且,也许您可​​以使用某种压缩或巧妙的算法来提高空间效率。但是,然后您要么在空间上进行交易,要么就处于针对某些情况进行了优化而针对其他情况进行了放慢的情况。

您可以优化常见的情况,但什么常见的情况可以在语言,应用领域等类型的代码是快速的视频游戏改变(数字运算,具有较大的输入密集的循环),可能比不同编译器最快的速度(遍历树,工作列表等)。

加分点:是否存在将函数编码的整数映射到本机整数(C中的short,int等)的编码?可能吗

不,这是不可能的。问题在于lambda演算不允许您对术语进行自省。当函数采用与教堂数字类型相同的参数时,它必须能够调用它,而无需检查该数字的确切定义。教会编码就是这样:您可以使用它们进行唯一的操作就是调用它们,并且可以模拟所有有用的东西,但这并非没有代价。

更重要的是,整数占据了每种可能的二进制编码。因此,如果将lambda表示为它们的整数,则无法表示非教会数字的lambda!或者,您将引入一个标志来表示lambda是否为数字,但是您想要的任何效率都可能超出标准。

编辑:自编写此文档以来,我已经意识到实现高阶函数的第三个选择:defunctionalization。在这里,每个函数调用都会变成一个大switch语句,具体取决于将哪个lambda抽象作为函数给出。这里的权衡是它是整个程序的转换:您不能单独编译各个部分,然后以这种方式链接在一起,因为您需要提前获得完整的lambda抽象集。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.