我正在尝试了解在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下编译时不会发生这种情况。
答案 0 :(得分: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的优化器不够聪明)。 / p>
答案 1 :(得分:5)
据我了解,runhaskell
几乎没有优化。它旨在快速加载和运行代码。如果它进行了更多优化,则代码开始运行需要更长时间。当然,在这种情况下,代码运行速度更快。
据我了解,如果代码的编译版本存在,那么runhaskell
将使用它。因此,如果性能对您很重要,请确保首先打开优化进行编译。 (我想您甚至可以将开关传递给runhaskell
以启用优化 - 您必须查看文档...)