2

我有一个我正在尝试优化的功能。这是一个更大的代码的一部分,我怀疑这个函数正在阻止在调用它的更高级别的函数中GHC取消装箱参数。Int所以,我写了一个简单的测试,考虑到两件事 - 了解核心,并尝试不同的事情来看看是什么让 GHC 将它拆箱,这样我就可以将这些课程应用到更大的代码中。这是cmp带有test函数包装器的函数:

{-# LANGUAGE BangPatterns #-}
module Cmp
( cmp,
  test )
where
import Data.Vector.Unboxed as U hiding (mapM_)
import Data.Word

cmp :: (U.Unbox a, Eq a) => U.Vector a -> U.Vector a -> Int -> Int -> Int
cmp a b !i !j = go a b 0 i j
               where
                 go v1 v2 !len !i !j| (i<n) && (j<m) && ((unsafeIndex v1 i) == (unsafeIndex v2 j)) = go v1 v2 (len+1) (i+1) (j+1)
                                    | otherwise = len
                   where
                    n = U.length a
                    m = U.length b
{-# INLINABLE cmp #-}

test ::  (U.Unbox a, Eq a) => U.Vector a -> U.Vector a -> U.Vector Int -> Int
test a b i = U.sum $ U.map (\x -> cmp a b x x) i

理想情况下,test应该调用具有以下签名的未装箱版本cmp(当然,如果我错了,请纠正我):

U.Vector a -> U.Vector a -> Int# -> Int# -> Int#

查看在ghc 7.6.1(命令行选项:)中生成的核心ghc -fforce-recomp -ddump-simpl -dsuppress-uniques -dsuppress-idinfo -dsuppress-module-prefixes -O2 -fllvm,我看到了这个 for 内部循环 for test- 下面的核心片段,并添加了我的评论:

-- cmp function doesn't have any helper functions with unboxed Int
--
cmp
  :: forall a.
     (Unbox a, Eq a) =>
     Vector a -> Vector a -> Int -> Int -> Int
...

-- This is the function that is called by test - it does keep the result
-- unboxed, but calls boxed cmp, and unboxes the result of cmp (I# y)
--
$wa
  :: forall a.
     (Unbox a, Eq a) =>
     Vector a -> Vector a -> Vector Int -> Int#
$wa =
  \ (@ a)
    (w :: Unbox a)
    (w1 :: Eq a)
    (w2 :: Vector a)
    (w3 :: Vector a)
    (w4 :: Vector Int) ->
    case w4
         `cast` (<TFCo:R:VectorInt> ; <NTCo:R:VectorInt>
                 :: Vector Int ~# Vector Int)
    of _ { Vector ipv ipv1 ipv2 ->
    letrec {
      $s$wfoldlM'_loop :: Int# -> Int# -> Int#
      $s$wfoldlM'_loop =
        \ (sc :: Int#) (sc1 :: Int#) ->
          case >=# sc1 ipv1 of _ {
            False ->
              case indexIntArray# ipv2 (+# ipv sc1) of wild { __DEFAULT ->
              let {
                x :: Int
                x = I# wild } in
              --
              -- Calls cmp and unboxes the Int result as I# y
              --
              case cmp @ a w w1 w2 w3 x x of _ { I# y ->
              $s$wfoldlM'_loop (+# sc y) (+# sc1 1)
              }
              };
            True -> sc
          }; } in
    $s$wfoldlM'_loop 0 0
}

-- helper function called by test - it calls $wa which calls boxed cmp
--
test1
  :: forall a.
     (Unbox a, Eq a) =>
     Vector a -> Vector a -> Vector Int -> Id Int
test1 =
  \ (@ a)
    (w :: Unbox a)
    (w1 :: Eq a)
    (w2 :: Vector a)
    (w3 :: Vector a)
    (w4 :: Vector Int) ->
    case $wa @ a w w1 w2 w3 w4 of ww { __DEFAULT ->
    (I# ww) `cast` (Sym <(NTCo:Id <Int>)> :: Int ~# Id Int)
    }

我将欣赏有关如何强制cmptest. 我试图严格化不同的论点,但这就像把厨房水槽扔给它一样,当然没有用。我希望利用这里学到的经验来解决更复杂代码中的装箱/拆箱性能问题。

另外,还有一个问题 - 我已经看到cast在核心中使用,但在 Haskell/GHC wiki 上没有找到任何核心参考来解释它是什么。这似乎是一种类型转换操作。我希望能解释它是什么,以及如何在test1上面的函数中解释它。

4

1 回答 1

2

现在我没有ghc,所以我的建议是口头的:

于 2013-06-04T22:53:50.593 回答