Haskell编译器如何决定是否在堆或堆栈上分配?

Haskell不具有显式内存管理功能,并且所有对象都按值传递,因此没有明显的引用计数或垃圾回收。 Haskell编译器通常如何决定是否生成在堆栈上分配的代码与在堆上为给定变量分配的代码? 它会一直堆积或堆栈为同一个函数跨不同的呼叫站点分配相同的变量吗? 当它分配时,它如何决定何时释放内存? 堆栈分配和释放仍然在C中执行相同的函数入口/出口模式吗?


当你调用这样的函数时

f 42 (g x y)

那么运行时行为如下所示:

p1 = malloc(2 * sizeof(Word))
p1[0] = &Tag_for_Int
p1[1] = 42
p2 = malloc(3 * sizeof(Word))
p2[0] = &Code_for_g_x_y
p2[1] = x
p2[2] = y
f(p1, p2)

也就是说,参数通常作为指向堆中对象的指针传递,就像在Java中一样,但与Java不同,这些对象可能代表暂停计算,又名thunk,如我们的示例中的( gxy / p2 )。 没有优化,这个执行模型效率很低,但有许多方法可以避免这些开销。

  • GHC做了很多内联和拆箱。 内联消除了函数调用开销,并且通常可以进一步优化。 拆箱意味着更改调用约定,在上面的示例中,我们可以直接传递42而不是创建堆对象p1

  • 严格性分析发现是否保证评估论证。 在这种情况下,我们不需要创建thunk,而是完全评估表达式,然后将最终结果作为参数传递。

  • 小对象(目前只有8位CharInt )被缓存。 也就是说,不是为每个对象分配一个新的指针,而是返回一个指向缓存对象的指针。 即使对象最初是在堆上分配的,垃圾收集器也会在以后重新删除它们(只有小的IntChar )。 由于对象是不可变的,所以这是安全的。

  • 有限的逃避分析。 对于本地函数,可能会在堆栈上传递一些参数,因为在外层函数返回时它们已知是死代码。

  • 编辑 :有关(更多)更多信息,请参阅“在库存硬件上实现惰性功能语言:无刺无标记G机器”。 本文使用“push / enter”作为调用约定。 较新版本的GHC使用“eval / apply”调用约定。 有关该切换的权衡和原因的讨论,请参阅“如何快速制作咖喱:推/输入与评估/应用”


    GHC唯一的东西就是评估上下文。 任何使用let / where绑定分配的内容以及所有数据构造函数和函数都存储在堆中。 懒惰的评估让你所知道的有关严格语言执行策略的一切都无关紧要。

    链接地址: http://www.djcxy.com/p/79139.html

    上一篇: How do Haskell compilers decide whether to allocate on the heap or the stack?

    下一篇: == '