4

我有一个系统可以生成决策树并将它们转换为嵌套的 Common Lispif语句,其中包含检查变量值是>=<=给定整数的谓词,例如

(LAMBDA (V1 V2)
  (IF (>= V1 2)
      (IF (<= V1 3)
          (IF (<= V2 3)
              (IF (>= V2 2) 16 (IF (>= V2 1) 6 0))
            (IF (<= V2 4) 10 0))
        (IF (<= V1 4)
            (IF (>= V2 1) (IF (<= V2 3) 6 0) 0)
          0))
    (IF (>= V1 1)
        (IF (>= V2 2) (IF (<= V2 4) 10 0) 0)
      0)))

然后我用它eval来编译 Lisp 代码,生成运行速度比解释原始决策树快得多的函数。然而,这个编译步骤花费了惊人的时间:一个包含 5000 个嵌套 if 的函数需要花费一分钟来编译(在 Powerbook 上的 Clozure Common Lisp 中),尽管生成 if 语句需要大约 100 毫秒。为什么这么简单的结构要花这么长时间?我可以做些什么来大大加快速度,也许是一些声明?我非常感谢您提供的任何指示。

4

2 回答 2

6

调用编译函数的实际可移植函数COMPILE

您可以通过,和- 的低optimize质量来告诉 Common Lisp 编译器投入更少的工作,这是否有任何影响取决于实现。speedspacedebugcompilation-speed

Clozure CL 编译器通常不是最聪明的,但相对较快。一般来说,我认为编译器维护者可能会给你更多关于如何加速编译的提示。一般我会找三个

  1. 告诉编译器少做一些工作:不进行类型推断、不优化代码、不生成调试信息、不节省空间……
  2. 如果有必要告诉编译器它必须推断出的东西——比如代替编译器的类型推断,在代码生成期间声明所有类型。但这意味着您实际上需要从类型声明中获得一些优势,例如提高运行时安全性或代码优化。
  3. 编译器本身可能有速度损失,这可能取决于源代码的大小。例如,如果它是二次的,如果我们将代码大小加倍,编译时间将增加四倍。只有编译器维护者可能知道在这些情况下该做什么——也许他们需要实现更有效的数据结构或类似的......

下一个选项是使用 Lisp 解释器。它们通常具有很少的定义时间开销 - 但代码通常在运行时运行得慢得多。在某些问题域中,可以采用混合方法:编译不经常更改的代码并解释经常更改的代码。

于 2018-09-12T11:56:28.890 回答
4

您当然可以(declare (optimize (compilation-speed 3))),并且可能会降低其他品质(请参阅http://clhs.lisp.se/Body/d_optimi.htm#optimize)。

但是,我猜编译缓慢是由编译器进行的优化引起的,因此结果在执行时似乎可能不会那么快。但也许不是,你必须进行实验。

我还会考虑您可以使用您的领域知识进行哪些优化。这方面的提示也可能来自分析disassemble生成函数的输出。

最后,如果不同值的数量不太大,也许您可​​以将决策树转换为查找表。

于 2018-09-12T11:41:59.847 回答