8

我试图了解在runhaskell.

有问题的程序是:

isFactor n = (0 ==) . (mod n)
factors x = filter (isFactor x) [2..x]
main = putStrLn $ show $ sum $ factors 10000000

当我运行它时,它需要 1.18 秒。

但是,如果我重新定义isFactor为:

isFactor n f = (0 ==) (mod n f)

然后程序需要 17.7 秒。

这是性能上的巨大差异,我希望这些程序是等效的。有人知道我在这里想念什么吗?

注意:在 GHC 下编译时不会发生这种情况。

4

2 回答 2

9

尽管功能应该相同,但它们的应用方式却有所不同。对于第一个定义,isFactor完全应用于调用站点isFactor x。在第二个定义中,它不是,因为现在isFactor明确地接受两个参数。

即使是最小的优化也足以让 GHC 看穿它并为两者创建相同的代码,但是如果你编译-O0 -ddump-simpl你可以确定,没有优化,这会有所不同(至少对于 ghc-7.2.1,YMMV 和其他版本)。

使用第一个isFactor,GHC 创建一个函数,该函数作为谓词传递给“GHC.List.Filter”,并调用mod 10000000(==)内联。对于第二个定义,发生的情况是,其中的大多数调用isFactor都是对类函数的 let-bound 引用,并且不会在 的多个调用之间共享isFactor。所以有很多完全没有必要的字典开销。

这几乎从来都不是问题,因为即使是默认的编译器设置也会对其进行优化,但是 runhaskell 显然甚至没有做那么多。即便如此,我偶尔也会结构化代码,someFun x y = \z ->因为我知道这someFun将被部分应用,这是保持调用之间共享的唯一方法(即 GHC 的优化器不够聪明)。

于 2012-02-17T12:48:53.627 回答
5

据我了解,runhaskell几乎没有优化。它旨在快速加载和运行代码。如果它进行了更多优化,您的代码将需要更长的时间才能开始运行。当然,在这种情况下,代码通过优化运行得更快。

据我了解,如果存在代码的编译版本,runhaskell则将使用它。因此,如果性能对您很重要,请确保首先打开优化进行编译。(我认为你甚至可以通过开关来runhaskell打开优化 - 你必须检查文档......)

于 2012-02-17T09:23:20.587 回答