Sal*_*Sal 5 performance unboxing haskell vector ghc
我试图调试性能问题作为更复杂的代码的一部分.似乎append我用来创建动态的,可增长的向量的(Int,Int,Int,Int)函数导致Int元组中的一个在被写入向量之前被装箱和取消装箱.我写了一个更简单的代码来重现这个问题 - 它似乎只有在我在append函数中添加向量增长功能时才会发生- 下面的示例代码(除了重现问题之外它没有做太多有用的工作),其后的片段core显示了值被装箱和取消装箱:
{-# LANGUAGE BangPatterns #-}
module Test
where
import Data.Vector.Unboxed.Mutable as MU
import Data.Vector.Unboxed as U hiding (mapM_)
import Control.Monad.ST as ST
import Control.Monad.Primitive (PrimState)
import Control.Monad (when)
import GHC.Float.RealFracMethods (int2Float)
import Data.STRef (newSTRef, writeSTRef, readSTRef)
import Data.Word
type MVI1 s = MVector (PrimState (ST s)) Int
type MVI4 s = MVector (PrimState (ST s)) (Int,Int,Int,Int)
data Snakev s = S {-# UNPACK #-}!Int
!(MVI4 s)
newVI1 :: Int -> Int -> ST s (MVI1 s)
newVI1 n x = do
a <- new n
mapM_ (\i -> MU.unsafeWrite a i x) [0..n-1]
return a
-- Growable array - we always append an element. It grows by factor of 1.5 if more capacity is needed
append :: Snakev s -> (Int,Int,Int,Int) -> ST s (Snakev s)
append (S i v) x = do
if i < MU.length v then MU.unsafeWrite v i x >> return (S (i+1) v)
else MU.unsafeGrow v (floor $! 1.5 * (int2Float $ MU.length v)) >>= (\y -> MU.unsafeWrite y i x >> return (S (i+1) y))
gridWalk :: Vector Word8 -> Vector Word8 -> MVI1 s -> MVI1 s -> Snakev s -> Int -> (Vector Word8 -> Vector Word8 -> Int -> Int -> Int) -> ST s (Snakev s)
gridWalk a b fp snodes snakesv !k cmp = do
let offset = 1+U.length a
xp = offset-k
snodep <- MU.unsafeRead snodes xp -- get the index of previous snake node in snakev array
append snakesv (snodep,xp,xp,xp)
{-#INLINABLE gridWalk #-}
Run Code Online (Sandbox Code Playgroud)
GHC生成一个append可供使用的版本gridWalk.该功能是$wa核心 - 请注意盒装的Int参数:
$wa
:: forall s.
Int#
-> MVI4 s
-> Int#
-> Int#
-> Int#
-> Int ======= Boxed value - one of (Int,Int,Int,Int) is boxed
-> State# s
-> (# State# s, Snakev s #)
$wa =
\ (@ s)
(ww :: Int#)
(ww1 :: MVI4 s)
(ww2 :: Int#)
(ww3 :: Int#)
(ww4 :: Int#)
(ww5 :: Int) === Boxed value
(w :: State# s) ->
....
....
of ipv12 { __DEFAULT ->
case (writeIntArray# ipv7 ww ww4 (ipv12 `cast` ...)) `cast` ...
of ipv13 { __DEFAULT ->
(# case ww5 of _ { I# x# ->
(writeIntArray# ipv10 ww x# (ipv13 `cast` ...)) `cast` ...
},
S (+# ww 1)
((MV_4
(+# y rb)
==== x below unboxed from arg ww5 ======
((MVector 0 x ipv1) `cast` ...)
((MVector 0 x1 ipv4) `cast` ...)
((MVector 0 x2 ipv7) `cast` ...)
((MVector 0 x3 ipv10) `cast` ...))
`cast` ...) #)
Run Code Online (Sandbox Code Playgroud)
gridWalk调用时输入值append:
=== function called by gridWalk ======
a :: forall s.
Vector Word8
-> Vector Word8
-> MVI1 s
-> MVI1 s
-> Snakev s
-> Int
-> (Vector Word8 -> Vector Word8 -> Int -> Int -> Int)
-> State# s
-> (# State# s, Snakev s #)
a =
\ (@ s)
(a1 :: Vector Word8)
_
_
(snodes :: MVI1 s)
(snakesv :: Snakev s)
(k :: Int)
_
(eta :: State# s) ->
case k of _ { I# ipv ->
case snodes `cast` ... of _ { MVector rb _ rb2 ->
case a1 `cast` ... of _ { Vector _ rb4 _ ->
let {
y :: Int#
y = -# (+# 1 rb4) ipv } in
case readIntArray# rb2 (+# rb y) (eta `cast` ...)
of _ { (# ipv1, ipv2 #) ->
case snakesv of _ { S ww ww1 ->
====== y boxed below before append called ======
$wa ww ww1 ipv2 y y (I# y) (ipv1 `cast` ...)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以,效果似乎是在插入到矢量之前的gridWalk装箱和装箱中的装箱.标记不会改变行为 - 那些盒装值只是在函数体中移动.append(Int,Int,Int,Int)append INLINEgridWalk
我将非常感谢如何将此值取消装箱.我想保留功能append(即,在超出容量时处理向量增长),同时重构它.
GHC版本是7.6.1.矢量版是0.10.
这只是一条评论。我想我会摆脱 tuple 参数(调整appendin的使用gridWalk),但结果是(仅)最后一个 Int 参数必须被敲击才能将所有内容拆箱,这看起来确实很奇怪:
append :: Snakev s -> Int -> Int -> Int -> Int -> ST s (Snakev s)
append (S i v) a b c !d = do
if i < len then do MU.unsafeWrite v i (a,b,c,d)
return $ S (i+1) v
else do y <- MU.unsafeGrow v additional
MU.unsafeWrite y i (a,b,c,d)
return $ S (i+1) y
where len = MU.length v
additional = floor (1.5 * int2Float len) -- this seems kind of bizarre
-- by the way; can't you stay inside Int?
-- 3 * (len `div` 2) or something
Run Code Online (Sandbox Code Playgroud)
另外,编辑,如果您将应用程序移到S (i+1)do 块之外,您会得到所有内容,但我不确定这是否让我们更接近采石场......:
append :: Snakev s -> Int -> Int -> Int -> Int -> ST s (Snakev s)
append (S i v) a b c d = do
if i < len then liftM (S (i+1)) $ do MU.unsafeWrite v i (a,b,c,d)
return v
else liftM ( S (i+1)) $ do y <- MU.unsafeGrow v zzz
MU.unsafeWrite y i (a,b,c,d)
return y
where len = MU.length v
zzz = floor (1.5 * int2Float len)
Run Code Online (Sandbox Code Playgroud)
但如果liftM被 替换,fmap我们就回到了单独的未装箱状态。如果liftM (S (1+i) 或 fmap (S (i+1)一直移到前面,事情就会顺利进行:
append (S i v) a b c d = S (i+1) <$> do ...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
297 次 |
| 最近记录: |