通过Core进行性能分析

以下代码在我的计算机上运行约1.5ms(使用GHC 8.0.1和-02编译):

import Criterion
import Data.Bits
import Data.Int
import Criterion.Main

main :: IO ()
main = defaultMain [bench "mybench" $ nf (mybench 3840) (0 :: Int)]

mybench :: Int -> Int -> Double
{-# INLINE mybench #-}
mybench n = go 0 n
  where go s i g | i == 0 = s
                 | otherwise =
                      let (w,_) = f 1 0 g
                          --w = f 1 0 g
                          f mag v gen | mag >= 18446744073709551616000 = (v,gen)
                          --f mag v gen | mag >= 18446744073709551616000 = v
                                      | otherwise = v' `seq` f (mag*18446744073709551616 :: Integer) v' gen where
                                            x = -8499970308474009078 :: Int
                                            v' = (v * 18446744073709551616 + (fromIntegral x + 9223372036854775808))

                          y = fromInteger ((-9223372036854775808) + w `mod` 18446744073709551616)
                          coef = (fromIntegral (9007199254740991 .&. (y::Int64)) :: Double) /  9007199254740992
                          z = 2.0 * (-0.5 + coef)
                      in go (z+s) (i-1) g

但是,如果我使用wf的注释交替,代码将运行在〜31μs! 这对我来说是令人惊讶的,因为我改变的很少,并且因为f每运行3,840次迭代(即代码几乎不用)会运行两次。

我去核心调查。 以下是缓慢版本和快速版本中-ddump-simpl的相关部分。

不幸的是,我无法从核心看到造成如此巨大差异的原因。 我看到的主要区别在于,在快速版本中,GHC已经意识到f不需要gen论证。 但肯定不能使45x / 2数量级的性能差异。

源代码有点人为设计(几个参数不需要或使用),所以我的主要问题是关于核心:我没有看到任何可能表明如此巨大性能差异的差异。 分析核心时我错过了什么? 作为后续,我可以在第一个/慢版本的源代码级别做些什么来使它像第二个/快速版本一样执行?


它看起来像在GHC的快速版本中解除了计算:

 y = fromInteger ((-9223372036854775808) + w `mod` 18446744073709551616)

走出go的定义。 只要看看modIntegerplusInteger在两个转储中的位置。

它看起来像在分配w = f 1 0 g它内联的定义f ,使其不必计算w每次调用go 。 更具体地说, f 1 0 g不依赖于任何参数go - 即。 sig ,所以它的计算可以被解除。

即使g在表达式f 1 0 g传递给f ,它实际上并没有被使用。

链接地址: http://www.djcxy.com/p/43177.html

上一篇: Performance Analysis via Core

下一篇: How much does Haskell/GHC memoize?