【问题标题】:Haskell performance tuningHaskell 性能调优
【发布时间】:2015-06-07 10:39:44
【问题描述】:

我对 Haskell 还是很陌生,为了更好地学习它,我开始到处解决问题,最终得到了这个 (project Euler 34)。

145 是一个奇怪的数字,因为 1! + 4! + 5! = 1 + 24 + 120 = 145。

找出所有数字之和等于其数字的阶乘之和。

注意:如 1! = 1 和 2! = 2 不是它们不包括在内的总和。

我写了一个 C 和一个 Haskell 蛮力解决方案。

有人能解释一下 Haskell 版本比 C 实现慢约 15 倍(约 0.450 秒 vs 约 6.5 秒),以及如何调整和加速 Haskell 解决方案?

unsigned int solve(){
unsigned int result = 0;
unsigned int i=10;
while(i<2540161){
    unsigned int sumOfFacts = 0;
    unsigned int number = i;
    while (number > 0) {
       unsigned int d = number % 10;
        number /= 10;
        sumOfFacts += factorial(d);
    }
    
    if (sumOfFacts == i)
        result += i;
    
    i++;
 }
 return result;
}

这里是haskell解决方案

--BRUTE FORCE SOLUTION
solve:: Int
solve = sum (filter (\x-> sfc x 0 == x) [10..2540160])

--sum factorial of digits
sfc :: Int -> Int -> Int
sfc 0 acc = acc
sfc n acc = sfc n' (acc+fc r)
    where
        n' = div n 10
        r  = mod n 10   --n-(10*n')
        fc 0 =1
        fc 1 =1
        fc 2 =2
        fc 3 =6
        fc 4 =24
        fc 5 =120
        fc 6 =720
        fc 7 =5040
        fc 8 =40320
        fc 9 =362880
    

【问题讨论】:

  • 你用-O编译了Haskell版本吗?
  • 一个用-O2编译的非常相似的程序在我的电脑上运行0.55秒
  • 和你的版本基本一样(0.54s)

标签: performance haskell


【解决方案1】:

首先,使用优化进行编译。使用ghc-7.10.1 -O2 -fllvm,Haskell 版本对我来说在 0.54 秒内运行。这已经很不错了。

如果我们想做得更好,我们应该首先将div替换为quot,将mod替换为remdivmod 做了一些额外的工作,因为它们以不同的方式处理负数的舍入。由于我们这里只有正数,我们应该切换到更快的函数。

其次,我们应该将fc 中的模式匹配替换为数组查找。 GHC 对Int 模式使用分支构造,并在案例数量足够大时使用二分搜索。通过查找,我们可以在这里做得更好。

新代码如下所示:

import qualified Data.Vector.Unboxed as V

facs :: V.Vector Int
facs =
  V.fromList [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

--BRUTE FORCE SOLUTION
solve:: Int
solve = sum (filter (\x-> sfc x 0 == x) [10..2540160])

--sum factorial of digits
sfc :: Int -> Int -> Int
sfc 0 acc = acc
sfc n acc = sfc n' (acc + V.unsafeIndex facs r)
    where
        (n', r) = quotRem n 10

main = print solve

它在我的电脑上运行 0.095 秒。

【讨论】:

  • “GHC 对模式使用分支结构”——正确,但不适用于 GHC HEAD,它现在使用跳转表:ghc.haskell.org/trac/ghc/ticket/10137
  • 你应该调用quotRem而不是quotrem
  • @Stefan 我想我可以,但在这种优化级别上没关系。
  • @JoachimBreitner 这真是太棒了!
  • @AndrásKovács 很好的答案!我应该尝试编译器优化!无论如何,您在我的笔记本电脑(相当旧的英特尔 centrino)上的解决方案 ghc 7.8.2 运行时间为 555 毫秒(比我的版本慢约 100 毫秒),并使用@stefan 建议的优化加速约 350。我将在我的工作站上尝试(并更新问题)。有人知道在这种情况下使用 -O2 进行的优化类型吗?
猜你喜欢
  • 2011-12-30
  • 2021-07-22
  • 2013-05-26
  • 2013-10-17
  • 2018-03-06
  • 2016-06-25
  • 2018-04-20
  • 2011-04-11
  • 1970-01-01
相关资源
最近更新 更多