【问题标题】:Why does adding a polymorphic type signature degrade performance?为什么添加多态类型签名会降低性能?
【发布时间】:2012-06-21 19:36:25
【问题描述】:

这是一个计算斐波那契数的简单函数:

fib :: [Int]
fib = 1 : 1 : zipWith (+) fib (tail fib)

在 ghci 中,我可以快速计算系列。事实上,一些实验表明计算运行在近似线性的时间内。

ghci> last $ take 100000 fib
354224848179261915075         -- takes under a second

如果我将类型签名改为多态:

fib :: Num a => [a]
fib = 1 : 1 : zipWith (+) fib (tail fib)

然后算法变慢。事实上,它现在似乎以指数时间运行!

切换到多态类型签名是否意味着列表在每个阶段都被完全重新计算?如果有,为什么?

【问题讨论】:

标签: performance haskell


【解决方案1】:

这是因为字典翻译。

fib :: [Int]

是顶级常量。

但是这个:

fib :: Num a => [a]
fib = 1 : 1 : zipWith (+) fib (tail fib)

只是一个顶级函数,它将在运行时应用于Num 字典。像这样:

fib d = 1 : 1 : zipWith (d.+) (fib d) (tail (fib d))

所以如果你在没有任何优化的情况下编译它,这样就不会发生专门化,你最终会得到指数时间 fib,因为列表是在每次函数调用时从头开始重建的——它不是惰性数据结构不再。

【讨论】:

    【解决方案2】:

    是的,多态类型签名意味着它在每个阶段都被重新计算。 ghc-7.4.2 生产的内核-O2

    lvl_rcZ :: GHC.Integer.Type.Integer
    [GblId, Str=DmdType]
    lvl_rcZ = __integer 1
    
    Rec {
    PolyFib.fib [Occ=LoopBreaker]
      :: forall a_a9W. GHC.Num.Num a_a9W => [a_a9W]
    [GblId, Arity=1, Str=DmdType L]
    PolyFib.fib =
      \ (@ a_aal) ($dNum_aam :: GHC.Num.Num a_aal) ->
        GHC.Types.:
          @ a_aal
          (GHC.Num.fromInteger @ a_aal $dNum_aam lvl_rcZ)
          (GHC.Types.:
             @ a_aal
             (GHC.Num.fromInteger @ a_aal $dNum_aam lvl_rcZ)
             (GHC.List.zipWith
                @ a_aal
                @ a_aal
                @ a_aal
                (GHC.Num.+ @ a_aal $dNum_aam)
                (PolyFib.fib @ a_aal $dNum_aam)
                (case PolyFib.fib @ a_aal $dNum_aam of _ {
                   [] -> GHC.List.tail1 @ a_aal;
                   : _ xs_abD -> xs_abD
                 })))
    end Rec }
    

    原因是缓存属于Num 的每种类型的斐波那契数列是不可行的,而fib 明确是一个多态值,因此它根本不会被缓存。

    如果您想至少为每种类型的计算缓存它,请使用本地列表

    pfibs :: Num a => [a]
    pfibs = res
      where
        res = 1 : 1 : zipWith (+) res (tail res)
    

    为每个计算进行缓存(所以pfibs !! 500 例如很快),因为现在列表在每个计算中都是单态的。它仍然会为每个查询重新计算(除非您将其绑定到单态名称),但不会为每个列表元素重新计算。

    *PolyFib> pfibs !! 999999 :: Int
    -4249520595888827205
    (0.31 secs, 137462088 bytes)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-30
      • 1970-01-01
      • 2017-07-07
      • 1970-01-01
      相关资源
      最近更新 更多