2012-04-26 19 views
7

से बाहर कोऑक्सिंग लूप-इनवेरिएंट कोड गति, मैं जीएचसी में निम्न स्तर के मैनुअल लूप ऑप्टिमाइज़ेशन के साथ संघर्ष कर रहा हूं। मेरे कार्यक्रम में कुछ लूप हैं जो संख्यात्मक गणना करते हैं। असली डेटा अन्य डेटा संरचनाओं में लपेटा गया है, और प्रोग्राम को "लूपिंग कंट्रोल फ्लो फ़ंक्शंस" और "गणना फ़ंक्शन" में इस तरह से विभाजित किया गया है कि कुछ डेटा संरचना फ़ील्ड आंतरिक लूप के अंदर पढ़ा जा रहा है। मैं चाहता हूं कि जीएचसी इन लूपों को आंतरिक लूप से बाहर ले जाए। यह देखने के लिए कि क्या हो रहा है, कोड का सरलीकृत संस्करण यहां दिया गया है।जीएचसी

data D = D !Double !C 
data C = C Double 

-- This function is called in every loop iteration. 
-- Parameter 'c' is loop-invariant. 
exampleLoopBody i a c = 
    case c of C b -> a + b * fromIntegral i 

-- The body of this function is a counted loop that should be optimized 
foo x = 
    case x 
    of D acc0 c -> 
    let loop i acc = 
      if i > 100 
      then acc 
      else loop (i+1) (exampleLoopBody i acc c) 
    in loop 0 acc0 

हर पाश यात्रा case c of C b मूल्यांकन करता है, लेकिन यह है कि अनावश्यक गणना है, क्योंकि c पाश-अपरिवर्तनीय है। मैं GHC पाश के बाहर एक निरर्थक मामले अभिव्यक्ति डालकर इसे बाहर उठा कर सकते हैं:

foo x = 
    case x 
    of D acc0 c -> 
    case c    -- This case statement inserted for optimization purposes 
    of C b -> b `seq` -- It will read 'b' outside of the loop 
     let loop i acc = 
      if i > 100 
      then acc 
      else loop (i+1) (exampleLoopBody i acc c) 
     in loop 0 acc0 

संकलक inlines exampleLoopBody। बाद में, आंतरिक मामला बयान अनावश्यक है और समाप्त हो जाता है:

foo x = 
    case x 
    of D acc0 c -> 
    case c 
    of C b -> b `seq` 
     let loop i acc = 
      if i > 100 
      then acc 
      else loop (i+1) (acc + b * fromIntegral i) -- The inlined case expression disappears 
     in loop 0 acc0 

seq का उद्देश्य यह सुनिश्चित करना है कि मामले अभिव्यक्ति मृत कोड नहीं है। seq चेक करता है कि b_|_ है। जीएचसी नोटिस करता है कि, b की गणना की गई है, इसलिए लूप बॉडी में उस मान का पुन: उपयोग करना उपयोगी है।

अब, यहां समस्या है: मैं वास्तव में सभी प्रासंगिक डेटा फ़ील्ड सख्त होना चाहता हूं। अगर मैं डेटा परिभाषा कठोरता एनोटेशन, इस तरह,

data C = C !Double 

सम्मिलित तो seq और case c of C b जहाँ तक GHC का सवाल है कोई असर नहीं। GHC उन्हें हटा देता है, और मैं इस मिल: हर यात्रा, जो सिर्फ मैं क्या से बचने के लिए कोशिश कर रहा था है में

foo x = 
    case x 
    of D acc0 c -> 
    let loop i acc = 
      if i > 100 
      then acc 
      else loop (i+1) (case c of C b -> acc + b * fromIntegral i) -- Evaluate the case in every iteration 
    in loop 0 acc0 

इस कोड का मूल्यांकन case c of C b

यदि मैं seq पर भरोसा नहीं कर सकता, तो मुझे नहीं पता कि लूप बॉडी के बाहर गणना करने के लिए b को और कैसे मजबूर करना है। क्या इस मामले में कुछ चाल है जिसका उपयोग मैं कर सकता हूं?

+0

कौन सा GHC संस्करण का उपयोग कर रहे हैं? मुझे 7.4.1 और 7.2.2 से प्रत्येक पुनरावृत्ति में 'केस' के बिना अच्छा कोर मिलता है। शुद्ध अनबॉक्स 'डबल # 'एस। –

+0

@DanielFischer मैं 7.0 का उपयोग कर रहा हूँ।3 और सरलीफायर के माध्यम से जाने के बाद 'डबल' में से एक को बॉक्स किया गया है। एफवाईआई, मेरे असली उपयोग मामले में वास्तव में स्थिर रूप से आकार वाले वैक्टर शामिल हैं जैसे 'कंस डबल (कंस डबल नील)'। क्या होता है यह देखने के लिए मैं इसे एक नए जीएचसी में चलाने की कोशिश कर सकता हूं। – Heatsink

+0

हम्म, मुझे 7.0.4 के साथ एक अनबॉक्सित लूप भी मिलता है। '$ Wfoo' कर्मचारी के पास एक बॉक्स किए गए तर्क (7.4.1 और 7.2.2 के साथ भी) है, लेकिन लूप स्वयं के अंदर 'letrec'ed है और एक अनबॉक्स किया गया' डबल # '(दूसरा' डबल # 'बनाया गया है स्थैतिक, यहां तक ​​कि)। –

उत्तर

2

आप तर्क उलटफेर और एक लैम्ब्डा में पाश संस्करण भागों ले जाने का प्रयास कर सकते हैं: तो तुम संचायक पैरामीटर हर मजबूर करने के लिए चाहते हो सकता है, इस कोड को इस समय एक बड़ी unevaluated अभिव्यक्ति बनाता

-- note the order of the arguments changed 
exampleLoopBody (C b) = 
    \i a -> a + b * fromIntegral i 

foo (D acc0 c) = 
    let 
     loopBody = exampleLoopBody c 
     loop i acc = 
      if i > 100 
     then acc 
     else loop (i+1) (loopBody i acc) 
    in loop 0 acc0 

इसके अलावा लूप के माध्यम से समय।

0

ऐसा लगता है कि मूल रूप से पूरे कारण newtype भाषा में रखा गया था। बस data C = C !Double से newtype C = C Double पर बदलें और कोड के बेवकूफ संस्करण को लिखें। प्रकार C के मानों पर अभिव्यक्ति मिटा दी जाएगी। एक तरफ ध्यान दें, कोड पैटर्न अपने उदाहरण में आप के रूप में:

case foo of 
    D acc0 c -> case c of 
     C b -> ... 

अधिक संक्षेप लिखा जा सकता है:

case foo of 
    D acc0 (C b) -> ... 
+0

प्रश्न पूछते समय मैंने अपनी समस्या को बहुत सरल बना दिया होगा । वास्तविक कार्यक्रम में, 'foo' एक बहुरूप कार्य है। 'C' का प्रकार भिन्न होता है और इसमें एक से अधिक फ़ील्ड हो सकते हैं। – Heatsink

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