performance – 在四元组的向量中取消装箱的盒装值
发布时间:2020-12-14 19:35:01 所属栏目:Java 来源:网络整理
导读:我试图调试性能问题作为更复杂的代码的一部分.似乎我用来创建动态,可增长的(Int,Int,Int)向量的追加函数导致元组中的一个Int在被写入向量之前被装箱和取消装箱.我写了一个更简单的代码来重现这个问题 – 它似乎只有在我在追加函数中添加向量增长功能时才会发
我试图调试性能问题作为更复杂的代码的一部分.似乎我用来创建动态,可增长的(Int,Int,Int)向量的追加函数导致元组中的一个Int在被写入向量之前被装箱和取消装箱.我写了一个更简单的代码来重现这个问题 – 它似乎只有在我在追加函数中添加向量增长功能时才会发生 – 下面的示例代码(除了重现问题之外它没有做太多有用的工作),其次是核心的片段,显示已装箱和未装箱的值:
{-# 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) 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) -> 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) {-#INLINABLE gridWalk #-} GHC生成一个append版本,用于gridWalk.该函数在核心中是$wa – 请注意盒装的Int参数: $wa :: forall s. Int# -> MVI4 s -> Int# -> Int# -> Int# -> Int ======= Boxed value - one of (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` ...) #) 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` ...) } } } } } 因此,在插入到(Int,Int)的向量之前,效果似乎是gridWalk中的值的装箱和附加的取消装箱.标记追加INLINE并不会改变行为 – 那些盒装值只会在gridWalk的函数体中移动. 我将非常感谢如何将此值取消装箱.我想保留append的功能(即,当超出容量时处理向量增长),同时重构它. GHC版本是7.6.1.矢量版本是0.10. 解决方法
这只是一个评论.我想我会摆脱tuple参数(在gridWalk中调整append的使用),但结果是(只)最后一个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,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 编辑,如果你将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,d) return v else liftM ( S (i+1)) $do y <- MU.unsafeGrow v zzz MU.unsafeWrite y i (a,d) return y where len = MU.length v zzz = floor (1.5 * int2Float len) 但是如果liftM被fmap取代,我们就会回到单独的未装箱状态.如果liftM(S(1 i)或fmap(S(i 1)一直移到前面,那么情况会顺利进行: append (S i v) a b c d = S (i+1) <$> do ... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |