से बाहर कोऑक्सिंग लूप-इनवेरिएंट कोड गति, मैं जीएचसी में निम्न स्तर के मैनुअल लूप ऑप्टिमाइज़ेशन के साथ संघर्ष कर रहा हूं। मेरे कार्यक्रम में कुछ लूप हैं जो संख्यात्मक गणना करते हैं। असली डेटा अन्य डेटा संरचनाओं में लपेटा गया है, और प्रोग्राम को "लूपिंग कंट्रोल फ्लो फ़ंक्शंस" और "गणना फ़ंक्शन" में इस तरह से विभाजित किया गया है कि कुछ डेटा संरचना फ़ील्ड आंतरिक लूप के अंदर पढ़ा जा रहा है। मैं चाहता हूं कि जीएचसी इन लूपों को आंतरिक लूप से बाहर ले जाए। यह देखने के लिए कि क्या हो रहा है, कोड का सरलीकृत संस्करण यहां दिया गया है।जीएचसी
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
को और कैसे मजबूर करना है। क्या इस मामले में कुछ चाल है जिसका उपयोग मैं कर सकता हूं?
कौन सा GHC संस्करण का उपयोग कर रहे हैं? मुझे 7.4.1 और 7.2.2 से प्रत्येक पुनरावृत्ति में 'केस' के बिना अच्छा कोर मिलता है। शुद्ध अनबॉक्स 'डबल # 'एस। –
@DanielFischer मैं 7.0 का उपयोग कर रहा हूँ।3 और सरलीफायर के माध्यम से जाने के बाद 'डबल' में से एक को बॉक्स किया गया है। एफवाईआई, मेरे असली उपयोग मामले में वास्तव में स्थिर रूप से आकार वाले वैक्टर शामिल हैं जैसे 'कंस डबल (कंस डबल नील)'। क्या होता है यह देखने के लिए मैं इसे एक नए जीएचसी में चलाने की कोशिश कर सकता हूं। – Heatsink
हम्म, मुझे 7.0.4 के साथ एक अनबॉक्सित लूप भी मिलता है। '$ Wfoo' कर्मचारी के पास एक बॉक्स किए गए तर्क (7.4.1 और 7.2.2 के साथ भी) है, लेकिन लूप स्वयं के अंदर 'letrec'ed है और एक अनबॉक्स किया गया' डबल # '(दूसरा' डबल # 'बनाया गया है स्थैतिक, यहां तक कि)। –