我试图了解在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下编译时不会发生这种情况。
最佳答案
尽管功能应该相同,但是它们的应用方式有所不同。根据第一个定义,isFactor
完全应用在调用站点isFactor x
上。在第二个定义中,它不是,因为现在isFactor
显式接受两个参数。
即使最小的优化也足以使GHC看到并为两者创建相同的代码,但是,如果您使用-O0 -ddump-simpl
进行编译,则可以确定不进行任何优化就可以有所作为(至少与ghc-7.2.1,YMMV与其他版本)。
使用第一个isFactor
,GHC创建一个作为谓词传递给“GHC.List.Filter”的函数,并插入对mod 10000000
和(==)
的调用。对于第二个定义,相反,发生的是isFactor
中的大多数调用是对类函数的绑定(bind)绑定(bind)引用,并且在isFactor
的多次调用之间不共享。因此,有很多词典开销是完全不必要的。
这几乎从来都不是问题,因为即使是默认的编译器设置也可以对其进行优化,但是runhaskell显然没有做太多事情。即使这样,我还是偶尔将代码结构化为someFun x y = \z ->
,因为我知道someFun
将被部分应用,这是保留调用之间共享的唯一方法(即GHC的优化器不够聪明)。