2011-06-09 7 views
19

में ट्रांसपोज़ और संचयी योग के साथ खराब प्रदर्शन मैंने हास्केल लाइब्रेरी रेपा में नीचे परिभाषित एक संचयी योग फ़ंक्शन विकसित किया है। हालांकि, इस कार्य को ट्रांज़ेक्शन ऑपरेशन के साथ संयोजित करते समय मैंने एक समस्या में भाग लिया है। ,रिपा

cumsum $ cumsum $ cumsum x 
transpose $ transpose $ transpose x 
transpose $ cumsum x 

लेकिन अगर मैं लिखने: निम्नलिखित कार्यों के सभी 3 अच्छी तरह से एक दूसरे के नीचे ले

cumsum $ transpose x 

प्रदर्शन horrendously गिरावट होती है। जबकि अलगाव में प्रत्येक व्यक्तिगत ऑपरेशन 1920x1080 छवि पर एक सेकंड के नीचे अच्छी तरह से लेता है, जब संयुक्त हो जाता है तो अब वे 30+ सेकंड लेते हैं ...

इस पर कोई विचार क्या हो सकता है? मेरा आंत मुझे बताता है कि इसमें देरी वाले सरणी के साथ कुछ करना है, सही समय पर मजबूर नहीं होना, आदि ... लेकिन मेरे पास अभी तक इसे ट्रैक करने के लिए पर्याप्त अनुभव नहीं है।

{-# LANGUAGE TypeOperators, FlexibleContexts, TypeFamilies #-} 

import Data.Array.Repa as Repa 

{-# INLINE indexSlice #-} 
indexSlice :: (Shape sh, Elt a) => Int -> Array (sh :. Int) a -> (sh :. Int) -> a 
indexSlice from arr (z :. ix) = arr `unsafeIndex` (z :. (ix + from)) 

{-# INLINE sliceRange #-} 
sliceRange :: (Slice sh, Shape sh, Elt a) => Int -> Int -> Array (sh :. Int) a -> Array (sh :. Int) a 
sliceRange from to arr = fromFunction (z :. (to - from + 1)) $ indexSlice from arr 
    where (z :. _) = extent arr 

{-# INLINE cumsum' #-} 
cumsum' :: (Slice (SliceShape sh), Slice sh, Shape (FullShape sh), Shape (SliceShape sh), Elt a, Num a) => 
    Array (FullShape sh :. Int) a -> t -> (sh :. Int) -> a 
cumsum' arr f (sh :. outer) = Repa.sumAll $ sliceRange 0 outer $ Repa.slice arr (sh :. All) 

{-# INLINE cumsum #-} 
cumsum :: (FullShape sh ~ sh, Slice sh, Slice (SliceShape sh), Shape sh, Shape (SliceShape sh), Elt a, Num a) => 
    Array (sh :. Int) a -> Array (sh :. Int) a 
cumsum arr = Repa.force $ unsafeTraverse arr id $ cumsum' arr 

उत्तर

25

एक पुस्तकालय implementor के दृष्टिकोण से, जिस तरह से इस डिबग करने के लिए संदिग्ध ऑपरेशन के लिए एक आवरण बनाने के लिए है, तो देखने के लिए कि संलयन काम किया है कोर कोड को देखो है।

-- Main.hs --------------------------------------------------- 
import Solver 
import Data.Array.Repa.IO.BMP 

main 
= do Right img  <- readImageFromBMP "whatever.bmp" 
     print $ cumsumBMP img 

-- Solver.hs -------------------------------------------------- 
{-# LANGUAGE TypeOperators, FlexibleContexts, TypeFamilies #-} 
module Solver (cumsumBMP) where 
import Data.Array.Repa as Repa 
import Data.Word 

{- all your defs -} 

{-# NOINLINE cumsumBMP #-} 
cumsumBMP :: Array DIM3 Word8 -> Array DIM3 Word8 
cumsumBMP img = cumsum $ transpose img 

मैं एक अलग मॉड्यूल में "solver" कोड डाल दिया है, इसलिए हम केवल परिभाषाओं हम के बारे में परवाह के लिए कोर कोड की खोजबीन करना है।

संकलित की तरह:

touch Solver.hs ; ghc -O2 --make Main.hs \ 
-ddump-simpl -dsuppress-module-prefixes -dsuppress-coercions > dump 

letrec कीवर्ड के लिए खोज cumsumBMP की परिभाषा में जाओ और। letrec के लिए खोज आंतरिक लूप को खोजने का एक त्वरित तरीका है।

नहीं बहुत दूर नीचे मैं इस देखें: (थोड़ा पुन: स्वरूपित)

case gen_a1tr 
of _ { 
    GenManifest vec_a1tv -> 
    case sh2_a1tc `cast` ... of _ { :. sh3_a1iu sh4_a1iv -> 
    case ix'_a1t9 `cast` ... of _ { :. sh1'_a1iz sh2'_a1iA -> 
    case sh3_a1iu `cast` ... of _ { :. sh5_X1n0 sh6_X1n2 -> 
    case sh1'_a1iz `cast` ... of _ { :. sh1'1_X1n9 sh2'1_X1nb -> 
    case sh5_X1n0    of _ { :. sh7_X1n8 sh8_X1na -> 
    ... 
    case sh2'1_X1nb   of _ { I# y3_X1nO -> 
    case sh4_a1iv    of _ { I# y4_X1nP -> 
    case sh2'_a1iA   of _ { I# y5_X1nX -> 
    ... 
    let { x3_a1x6 :: Int# [LclId] 
     x3_a1x6 = 
     +# 
      (*# 
      (+# 
       (*# 
        y1_a1iM 
        y2_X1nG) 
       y3_X1nO) 
      y4_X1nP) 
      y5_X1nX } in 
    case >=# 
      x3_a1x6 
      0 
    of ... 

आपदा! x3_a1x6 बाध्यकारी स्पष्ट रूप से कुछ उपयोगी काम (गुणा, जोड़ों और इसी तरह) कर रहा है लेकिन यह अनलॉकिंग संचालन की एक लंबी श्रृंखला में लपेटा गया है जिसे प्रत्येक लूप पुनरावृत्ति के लिए भी निष्पादित किया जाता है। इससे भी बदतर यह है कि यह प्रत्येक पुनरावृत्ति पर सरणी की लंबाई और चौड़ाई (आकार) को अनबॉक्सिंग कर रहा है, और यह जानकारी हमेशा एक जैसी होगी। जीएचसी वास्तव में लूप से इन केस एक्सप्रेशन को फ्लोट करना चाहिए, लेकिन यह अभी तक नहीं है। यह Issue #4081 on the GHC trac का एक उदाहरण है, जो उम्मीद है कि जल्द ही कुछ समय तय किया जाएगा।

आसपास के काम आने वाले सरणी में deepSeqArray लागू करना है। यह शीर्ष स्तर (लूप के बाहर) पर अपने मूल्य की मांग रखता है जो जीएचसी को पता चलता है कि मामले के मैचों को और आगे बढ़ाना ठीक है। cumsumBMP की तरह एक समारोह के लिए, हम भी उम्मीद भेजे सरणी पहले से ही प्रकट हो सकता है, तो हम इस के लिए एक स्पष्ट मामला मैच जोड़ सकते हैं:

{-# NOINLINE cumsumBMP #-} 
cumsumBMP :: Array DIM3 Word8 -> Array DIM3 Word8 
cumsumBMP [email protected](Array _ [Region RangeAll (GenManifest _)]) 
    = img `deepSeqArray` cumsum $ transpose img 

फिर से संकलन, भीतरी पाश अब ज्यादा बेहतर लग रहा है:

letrec { 
$s$wfoldlM'_loop_s2mW [...] 
    :: Int# -> Word# -> Word# [...] 
$s$wfoldlM'_loop_s2mW = 
    \ (sc_s2mA :: Int#) (sc1_s2mB :: Word#) -> 
    case <=# sc_s2mA a_s2ji of _ { 
     False -> sc1_s2mB; 
     True -> 
     $s$wfoldlM'_loop_s2mW 
      (+# sc_s2mA 1) 
      (narrow8Word# 
      (plusWord# 
       sc1_s2mB 
       (indexWord8Array# 
        rb3_a2gZ 
        (+# 
         rb1_a2gX 
         (+# 
         (*# 
          (+# 
           (*# 
            wild19_X1zO 
            ipv1_X1m5) 
           sc_s2mA) 
          ipv2_X1m0) 
         wild20_X1Ct))))) 
    }; } in 

यह एक तंग, पूंछ रिकर्सिव लूप है जो केवल आदिम परिचालन का उपयोग करता है। बशर्ते आप -fllvm -optlo-O3 के साथ संकलित करें, ऐसा कोई कारण नहीं है जो समकक्ष सी प्रोग्राम के रूप में तेज़ी से नहीं चलेगा।

वहाँ एक मामूली हिचकी जब यह हालांकि चल रहा है:

desire:tmp benl$ ./Main 
Main: Solver.hs:(50,1)-(51,45): Non-exhaustive patterns in function cumsumBMP 

यह सिर्फ हमें याद दिलाता है कि हम cumsumBMP कॉल करने से पहले सरणी के लिए मजबूर करने की जरूरत है।

-- Main.hs --------------------------------------------------- 
... 
import Data.Array.Repa as Repa 
main 
= do Right img  <- readImageFromBMP "whatever.bmp" 
     print $ cumsumBMP $ Repa.force img 

सारांश में:

  1. आप जोड़ने की जरूरत है कुछ deepSeqArray और अपने शीर्ष स्तर पर गूप मिलान कार्यों GHC में एक वर्तमान नाकामयाबी के आसपास काम करने के लिए पैटर्न। यह द्वारा cumsumBMP फ़ंक्शन के अंतिम संस्करण द्वारा प्रदर्शित किया गया है। यदि आप जीएचसी मुख्यालय को जल्द ही ठीक करना चाहते हैं तो अपने आप को Issue #4081 on the GHC trac पर एक सीसी के रूप में जोड़ें। जब यह तय किया जाता है तो रेपा कार्यक्रम बहुत सुंदर होंगे।
  2. आपको प्रत्येक फ़ंक्शन में गोद जोड़ने की आवश्यकता नहीं है। इस उदाहरण में मुझे indexSlice और दोस्तों को स्पर्श करने की आवश्यकता नहीं थी। सामान्य नियम force, fold या sumAll का उपयोग करने वाले कार्यों में जाने के लिए है। ये फ़ंक्शंस उन वास्तविक लूप को तुरंत चालू करते हैं जो सरणी डेटा पर काम करते हैं, यानी, वे एक देरी सरणी को एक मैनिफेस्ट मान में परिवर्तित करते हैं।
  3. रेपा कोड के एक टुकड़े का प्रदर्शन उस संदर्भ द्वारा उतना ही निर्धारित किया जाता है जिसमें इसे वास्तविक कोड के रूप में उपयोग किया जाता है। यदि आप अपने शीर्ष स्तर के कार्यों को देरी से गुजरते हैं तो वे बहुत धीमे चलेंगे। The Repa Tutorial में इसकी और चर्चा है।
  4. रेपा-आईओ लाइब्रेरी के साथ पढ़ने वाली बीएमपी फाइलें पूर्व-मजबूर नहीं हैं, इसलिए आपको उपयोग से पहले उन्हें मजबूर करने की आवश्यकता है। यह शायद गलत डिफ़ॉल्ट है, इसलिए मैं इसे अगले संस्करण में बदल दूंगा।
+0

वाह, मैं आपको एक बियर साथी का भुगतान करता हूं। उत्तर के लिए धन्यवाद, यह बेहद उपयोगी था। यह पुस्तकालय (रेपा) बकाया है। मैं निश्चित रूप से अंक # 4081 पर सीसी में फेंक दूंगा। जिस सीवी लाइब्रेरी पर मैं काम कर रहा हूं वह गिटूब और अंततः हैकेज पर जा रहा है, इसलिए यह सभी के लिए एक लाभ होगा। – nightski