26

我正在阅读 Simon Peyton Jones 等人撰写的论文。命名为“按规则行事:重写作为 GHC 中一种实用的优化技术”。在第二部分,即“基本思想”中,他们写道:

考虑熟悉的map函数,它将函数应用于列表的每个元素。用 Haskell 编写,map如下所示:

map f []     = []
map f (x:xs) = f x : map f xs

现在假设编译器遇到以下调用map

map f (map g xs)

我们知道这个表达式等价于

map (f . g) xs

(其中“.”是函数组合),我们知道后一种表达式比前一种更有效,因为没有中间列表。但是编译器没有这样的知识。

一种可能的反驳是编译器应该更聪明——但程序员总是知道编译器无法弄清楚的事情。另一个建议是:允许程序员将这些知识直接传达给编译器。这就是我们在这里探索的方向。

我的问题是,为什么我们不能让编译器更智能?作者说“但程序员总是知道编译器无法弄清楚的事情”。但是,这不是一个有效的答案,因为编译器确实可以计算出它map f (map g xs)等价于map (f . g) xs,这是如何:

map f (map g xs)
  1. map g xs与 统一map f [] = []

    因此map g [] = []

  2. map f (map g []) = map f [].

    map f []与 统一map f [] = []

    因此map f (map g []) = []

  3. map g xs与 统一map f (x:xs) = f x : map f xs

    因此map g (x:xs) = g x : map g xs

  4. map f (map g (x:xs)) = map f (g x : map g xs).

    map f (g x : map g xs)与 统一map f (x:xs) = f x : map f xs

    因此map f (map g (x:xs)) = f (g x) : map f (map g xs)

因此,我们现在有规则:

map f (map g [])     = []
map f (map g (x:xs)) = f (g x) : map f (map g xs)

如您所见f (g x), is just(f . g)并且map f (map g xs)正在被递归调用。这正是 的定义map (f . g) xs。这种自动转换的算法似乎很简单。那么为什么不实现这个而不是重写规则呢?

4

3 回答 3

34

激进的内联可以得出许多重写规则是简写的等式。不同之处在于内联是“盲目的”,因此您事先不知道结果是更好还是更差,或者即使它会终止。

然而,重写规则可以做完全不明显的事情,基于关于程序的更高级别的事实。将重写规则视为向优化器添加新公理。通过添加这些,您可以应用更丰富的规则集,从而更容易应用复杂的优化。

例如,流融合改变了数据类型表示。这不能通过内联来表达,因为它涉及表示类型的更改(我们根据StreamADT 重新构建优化问题)。易于在重写规则中声明,仅靠内联是不可能的。

于 2014-11-09T11:42:04.110 回答
7

在我的学生 Johannes Bader 的学士论文中研究了这方面的内容:Finding Equations in Functional ProgramsPDF 文件)。

在某种程度上,这当然是可能的,但是

  • 这很棘手。从某种意义上说,找到这样的方程就像在定理证明器中找到证明一样困难,并且
  • 它通常不是很有用,因为它倾向于找到程序员很少直接编写的方程式。

然而,在其他转换(例如内联和各种形式的融合)之后进行清理很有用。

于 2014-11-09T13:14:20.800 回答
1

这可以被视为在特定情况下平衡期望与在一般情况下平衡期望之间的平衡。这种平衡可能会产生有趣的情况,您可以知道如何更快地做出某些事情,但如果您不这样做,通常对语言来说会更好。

在您给出的结构中的特定地图情况下,计算机可以找到优化。但是,相关的结构呢?如果函数不是映射怎么办?如果有一个额外的间接层,比如一个返回 map 的函数。在这些情况下,编译器无法轻松优化。这是一般情况下的问题。

如果您确实优化了特殊情况,会出现两种结果之一

  • 没有人依赖它,因为他们不确定它是否存在。在这种情况下,像你引用的那样的文章会被写出来
  • 人们确实开始依赖它,现在每个开发人员都被迫记住“在此配置中完成的地图会自动转换为我的快速版本,但如果我在此配置中执行此操作,我不会。” 这开始操纵人们使用语言的方式,实际上会降低可读性!

鉴于开发人员需要在一般情况下考虑此类优化,我们希望看到开发人员在简单情况下进行这些优化,从而减少对优化的需求!

现在,如果事实证明您感兴趣的特定情况占了 Haskell 中世界代码库的 2% 之类的大量内容,那么应用您的特殊情况优化将会有更强有力的论据。

于 2014-11-09T20:06:40.040 回答