2013-06-05 7 views
5

एक प्रदर्शन समस्या है जिसे मैं एक अधिक जटिल कोड के हिस्से के रूप में डीबग करने का प्रयास कर रहा हूं। ऐसा लगता है कि append फ़ंक्शन जिसका उपयोग मैं (Int,Int,Int,Int) के गतिशील, बढ़ने योग्य वेक्टर बनाने के लिए कर रहा हूं, वेक्टर में लिखे जाने से पहले बॉक्सिंग और अनबॉक्स किए जाने वाले टुपल में Int में से एक का कारण बन रहा है। मैंने एक सरल कोड लिखा जो इस मुद्दे को पुन: उत्पन्न करता है - ऐसा लगता है कि जब मैं append फ़ंक्शन में वेक्टर विकास कार्यक्षमता जोड़ता हूं - नीचे नमूना कोड (यह समस्या को पुन: उत्पन्न करने के अलावा बहुत उपयोगी काम नहीं करता है), उसके बाद core के स्निपेट्स दिखाने के मूल्य बॉक्सिंग और अनबॉक्स्ड जा रहा है:चार tuples के वेक्टर में अनबॉक्सिंग बॉक्स किए गए मान

{-# 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 #-} 

GHC gridWalk में उपयोग के लिए append का एक संस्करण उत्पन्न करता है। यही कारण है कि समारोह कोर में $wa है - कृपया बॉक्सिंग इंट तर्क पर ध्यान दें:

$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` ...) #) 

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` ...) 
    } 
    } 
    } 
    } 
    } 

तो, प्रभाव gridWalk में मूल्य के मुक्केबाजी हो रहा है और (Int,Int,Int,Int) के वेक्टर में सम्मिलन से पहले append में अनबॉक्सिंग। appendINLINE चिह्नित करने से व्यवहार में कोई बदलाव नहीं आता है - उन बॉक्स किए गए मान केवल gridWalk के फ़ंक्शन बॉडी में स्थानांतरित होते हैं।

मैं इस मूल्य को अनबॉक्स किए जाने के तरीके पर पॉइंटर्स की सराहना करता हूं। मैं इसे रीफैक्टर करते समय append (यानी, क्षमता बढ़ने पर वेक्टर वृद्धि को संभालने) की कार्यक्षमता रखना चाहूंगा।

GHC संस्करण 7.6.1 है। वेक्टर संस्करण 0.10 है।

+0

मैं क्यों यह बॉक्सिंग है पता नहीं है, और मैं कर रहा हूँ रात और बियर के इस समय पता लगाने की कोशिश नहीं कर रहा है, लेकिन 'संलग्न करें (एस iv)! x @ (_, _, _,! _) = ...' अंतिम अनबॉक्स भी हो जाता है। निश्चित रूप से फिश लग रहा है, हालांकि, टिकट खोलने लायक हो सकता है। –

+0

@ डैनियल फिशर, हाँ, आप सख्त पैटर्न के साथ अनबॉक्सिंग के बारे में सही हैं। मुझे जीएचसी ट्रैक के लिए पुन: उत्पन्न करने के लिए एक आसान मामला मिलेगा। – Sal

उत्तर

3

यह सिर्फ एक टिप्पणी है। मुझे लगा कि मैं ट्यूपल तर्क से छुटकारा पाउंगा (appendgridWalk में 0) का उपयोग समायोजित करना होगा, लेकिन नतीजा यह है कि (केवल) अंतिम इंट तर्क को सब कुछ अनबॉक्स करने के लिए बैंग किया जाना चाहिए, जो अजीब लगता है:

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 

संपादित करें, यह भी, तुम सब कुछ अनबॉक्स्ड यदि आप ब्लॉक के बाहर S (i+1) के आवेदन ले जाते हैं, लेकिन मुझे यकीन है कि हमें खदान के करीब हो जाता है नहीं कर रहा हूँ मिलता है ...:

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)  

लेकिन यदि liftM प्रतिस्थापित है घ fmap से हम वापस में अकेला अनबॉक्स्ड कर रहे हैं हालात जाना अच्छी तरह से करता है, तो liftM (S (1+i)याfmap (S (i+1) सामने से बाहर सभी तरह ले जाया जाता है:।

append (S i v) a b c d = S (i+1) <$> do ... 
+0

हां, ट्यूपल पैटर्न में बैंग को मजबूर करने से भी समस्या ठीक हो जाती है। मैं यह देखने के लिए टिकट दर्ज करूंगा कि जीएचसी मुख्यालय क्या कहता है। – Sal

+0

सिर के लिए धन्यवाद। मैंने वेक्टर लाइब्रेरी के लिए एक बग टिकट दायर किया है। लिफ्टएम वर्कअराउंड के कारण उत्तर के रूप में आपकी टिप्पणी को स्वीकार करना। – Sal

+0

@marc_s, यहां "खदान" जानवरों द्वारा शिकार किए जाने वाले उद्देश्य के रूप में कथित तौर पर पीछा करने के उद्देश्य से संदर्भित करता है। यह एक त्रुटि नहीं है। – dfeuer

संबंधित मुद्दे