编译时替换的示例使程序恶化

时间:2016-12-16 00:59:25

标签: compiler-optimization lambda-calculus

想象一下一个类型化的lambda演算的天真编译器(以排除一个相当痛苦的非终止和隐式递归问题),它使用规范化(包括lambdas下的替换)作为"优化"。

对于简单程序,当大多数或所有变量只使用一次时,规范化会导致程序更短更快。

对我而言,显而易见的是"一般来说,这不是一个好主意。也就是说,随着规范化减少了共享,有些术语因优化而变得更糟。该术语有2次乘法

\x -> let a = x * x in a * a

获得"优化"成

\x -> (x * x) * (x * x)

其中3个。

如何构建一个任意恶化的例子?是否有一个术语在标准化时可能会溢出RAM?

我们正在使用具有强规范化的类型系统,因此不可能发散,例如在具有常数和delta规则的系统F的合适子集中。

或者使用"免费"添加mul等常量的方法,例如

\mul x -> let a = mul x x in mul a a

因此,不是添加常量,而是在运行时提供额外的参数"。

这个问题似乎属于SE计算机科学,但IMO确实是一个入门级的问题,所以我认为这在这里更合适。

1 个答案:

答案 0 :(得分:2)

如何将略微修改过的函数堆叠在自身之上,如下所示:

p:nat->nat->nat - 不透明的常量(或参数)。

q:(nat->nat->nat)->nat->nat->nat = \f:(nat->nat->nat).(\a b:nat.f (f a b) (f a b))

q p => \a b.p (p a b) (p a b)

q (q p) => \c d.q p (q p c d) (q p c d) 
  => \c d.q p (p (p c d) (p c d)) (p (p c d) (p c d))
  => \c d.p (p [p (p (p c d) (p c d))] [p (p (p c d) (p c d))]) (p [p (p (p c d) (p c d))] [p (p (p c d) (p c d))])

q (q (q p))扩展到一个巨大的术语

它以指数方式增长。您可以在Coq中验证它:

Section Expand.

Variable nat:Type.

Variable p:nat->nat->nat.

Definition q:(nat->nat->nat)->nat->nat->nat :=
  fun f:(nat->nat->nat) => fun a b:nat => f (f a b) (f a b).

Eval compute in (q p).
(*
  = fun a b : nat => p (p a b) (p a b)
     : nat -> nat -> nat
*)

Eval compute in (q (q p)).
(*
  = fun a b : nat =>
       p (p (p (p a b) (p a b)) (p (p a b) (p a b)))
         (p (p (p a b) (p a b)) (p (p a b) (p a b)))
     : nat -> nat -> nat
*)

Eval compute in (q (q (q p))).
(*
     = fun a b : nat =>
       p
         (p
            (p
               (p
                  (p (p (p (p a b) (p a b)) (p (p a b) (p a b)))
                     (p (p (p a b) (p a b)) (p (p a b) (p a b))))
                  (p (p (p (p a b) (p a b)) (p (p a b) (p a b)))
                 =============SKIPPED LOTS OF LINES==========
                  (p (p (p (p a b) (p a b)) (p (p a b) (p a b)))
                     (p (p (p a b) (p a b)) (p (p a b) (p a b)))))))
     : nat -> nat -> nat
*)

但是Haskell,由于它的懒惰和分享,能够非常快速地计算甚至大的术语(在几分之一秒内):

Prelude> q f a b = f (f a b) (f a b)
Prelude> (q $ q $ q (+)) 1 1
256
Prelude> (q $ q $ q $ q (+)) 1 1
65536
Prelude> (q $ q $ q $ q $ q (+)) 1 1
4294967296
Prelude> (q $ q $ q $ q $ q $ q (+)) 1 1
18446744073709551616
Prelude> (q $q $ q $ q $ q $ q $ q (+)) 1 1
340282366920938463463374607431768211456
Prelude> (q $ q $ q $ q $ q $ q $ q $ q (+)) 1 1
115792089237316195423570985008687907853269984665640564039457584007913129639936
Prelude> (q $ q $ q $ q $ q $ q $ q $ q $ q (+)) 1 1
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096