केवल 22Mb की कुल मेमोरी उपयोग के बावजूद Haskell थ्रेड्स ढेर अतिप्रवाह?


114

मैं एक किरण-अनुरेखक को समानांतर बनाने की कोशिश कर रहा हूं। इसका मतलब है कि मेरे पास छोटी संगणना की बहुत लंबी सूची है। वेनिला कार्यक्रम 67.98 सेकंड में विशिष्ट दृश्य और 13 एमबी कुल मेमोरी उपयोग और 99.2% उत्पादकता पर चलता है।

अपने पहले प्रयास में मैंने parBuffer50 के बफर आकार के साथ समानांतर रणनीति का उपयोग किया । मैंने चुना parBufferक्योंकि यह सूची के माध्यम से चलता है जितनी तेजी से स्पार्क्स का सेवन किया जाता है, और इस तरह की सूची की रीढ़ को मजबूर नहीं करता है parList, जो बहुत अधिक मेमोरी का उपयोग करेगा चूंकि सूची बहुत लंबी है। इसके साथ -N2, यह 100.46 सेकंड और 14 एमबी कुल मेमोरी उपयोग और 97.8% उत्पादकता के समय में चला गया। स्पार्क जानकारी है:SPARKS: 480000 (476469 converted, 0 overflowed, 0 dud, 161 GC'd, 3370 fizzled)

फ़िज़ल्ड स्पार्क्स का बड़ा अनुपात इंगित करता है कि स्पार्क्स की ग्रैन्युलैरिटी बहुत छोटी थी, इसलिए अगली बार मैंने रणनीति का उपयोग करने की कोशिश की parListChunk, जो सूची को विखंडू में विभाजित करती है और प्रत्येक चंक के लिए एक स्पार्क बनाती है। मुझे चंक साइज़ के साथ सबसे अच्छे परिणाम मिले 0.25 * imageWidth। कार्यक्रम 93.43 सेकंड में चला गया और कुल मेमोरी उपयोग की 236 एमबी और 97.3% उत्पादकता है। चिंगारी जानकारी है: SPARKS: 2400 (2400 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)। मेरा मानना ​​है कि बहुत अधिक मेमोरी का उपयोग होता है क्योंकि parListChunkसूची की रीढ़ को मजबूर करता है।

फिर मैंने अपनी रणनीति लिखने की कोशिश की कि आलस ने सूची को विखंडू में विभाजित कर दिया और फिर विखंडू को पारित कर दिया parBufferऔर परिणामों को संक्षिप्त कर दिया।

 concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map colorPixel pixels))

यह 95.99 सेकंड और 22MB कुल मेमोरी उपयोग और 98.8% उत्पादकता में चला। यह इस अर्थ में सफल था कि सभी स्पार्क्स को परिवर्तित किया जा रहा है और मेमोरी का उपयोग बहुत कम है, हालांकि गति में सुधार नहीं हुआ है। यहाँ Eventlog प्रोफ़ाइल के भाग की एक छवि है।ईवेंट लॉग प्रोफ़ाइल

जैसा कि आप देख सकते हैं कि ढेर के ढेर के कारण धागे बंद हो रहे हैं। मैंने जोड़ने की कोशिश की +RTS -M1Gजो डिफ़ॉल्ट ढेर के आकार को 1Gb तक बढ़ाता है। नतीजे नहीं बदले। मैंने पढ़ा कि हास्केल मुख्य धागा ढेर से मेमोरी का उपयोग करेगा यदि उसका स्टैक ओवरफ्लो होता है, तो मैंने डिफ़ॉल्ट स्टैक का आकार बढ़ाने की भी कोशिश की +RTS -M1G -K1Gलेकिन इसका भी कोई प्रभाव नहीं पड़ा।

क्या यहां कुछ और है जिसके लिए मैं कोशिश कर सकता हूं? मैं मेमोरी उपयोग या ईवेंटलॉग के लिए अधिक विस्तृत रूपरेखा जानकारी पोस्ट कर सकता हूं यदि आवश्यक हो, तो मैंने यह सब शामिल नहीं किया क्योंकि यह बहुत सारी जानकारी है और मुझे नहीं लगता कि इसमें सभी को शामिल करना आवश्यक था।

संपादित करें: मैं हास्केल आरटीएस मल्टीकोर समर्थन के बारे में पढ़ रहा था , और यह प्रत्येक कोर के लिए एक एचईसी (हास्केल एक्ज़ेक्यूशन संदर्भ) होने के बारे में बात करता है। प्रत्येक एचईसी में अन्य बातों के अलावा, एक आवंटन क्षेत्र (जो एक एकल साझा ढेर का एक हिस्सा है) शामिल है। जब भी किसी एचईसी के आवंटन क्षेत्र को समाप्त किया जाता है, तो कचरा संग्रह किया जाना चाहिए। यह नियंत्रित करने के लिए एक आरटीएस विकल्प प्रतीत होता है , -ए। मैंने -A32M की कोशिश की, लेकिन कोई अंतर नहीं देखा।

EDIT2: यहाँ इस सवाल के लिए समर्पित एक गितुब रेपो की एक कड़ी है । मैंने रूपरेखा फ़ोल्डर में प्रोफाइलिंग परिणामों को शामिल किया है।

EDIT3: यहां प्रासंगिक बिट कोड है:

render :: [([(Float,Float)],[(Float,Float)])] -> World -> [Color]
render grids world = cs where 
  ps = [ (i,j) | j <- reverse [0..wImgHt world - 1] , i <- [0..wImgWd world - 1] ]
  cs = map (colorPixel world) (zip ps grids)
  --cs = withStrategy (parListChunk (round (wImgWd world)) rdeepseq) (map (colorPixel world) (zip ps grids))
  --cs = withStrategy (parBuffer 16 rdeepseq) (map (colorPixel world) (zip ps grids))
  --cs = concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map (colorPixel world) (zip ps grids)))

ग्रिड यादृच्छिक फ़्लोट्स होते हैं जो कि पूर्वनिर्मित और कलरपिक्सल द्वारा उपयोग किए जाते हैं। इस प्रकार colorPixelहै:

 colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color

2
क्या आप जहाँ आप कोशिश कर रहे हैं, वही सटीक प्रतिबद्धता प्रदान कर सकते हैं concat $ withStrategy …? मैं इस व्यवहार को पुन: पेश नहीं कर सकता 6008010, जो आपके संपादन के लिए निकटतम प्रतिबद्धता है।
जीटा

3
मैंने एक समर्पित रेपो बनाया ताकि मैं गलती से उसमें इधर-उधर न फँसूँ। मैंने सभी प्रोफाइलिंग जानकारी को भी शामिल किया है।
जस्टिन रेमंड

@dfeuer मैं जब मैंने कहा कि मेरी अपनी रणनीति को परिभाषित मतलब नहीं था Strategy। एक बेहतर शब्द चुनना चाहिए था। इसके अलावा, ढेर अतिप्रवाह मुद्दा भी parListChunkऔर साथ होता parBufferहै।
जस्टिन रेमंड

जवाबों:


2

आपकी समस्या का समाधान नहीं है, लेकिन कारण के लिए एक संकेत:

स्मृति के पुन: उपयोग में हास्केल बहुत रूढ़िवादी लगता है और जब दुभाषिया मेमोरी ब्लॉक को पुनः प्राप्त करने की क्षमता देखता है, तो वह इसके लिए जाता है। आपकी समस्या वर्णन यहाँ वर्णित मामूली GC व्यवहार (नीचे) https://wiki.haskell.org/GHC/Memory_Management पर फिट बैठता है ।

नए डेटा 512kb "नर्सरी" में आवंटित किए गए हैं। एक बार जब यह समाप्त हो जाता है, तो "मामूली जीसी" होता है - यह नर्सरी को स्कैन करता है और अप्रयुक्त मूल्यों को मुक्त करता है।

इसलिए यदि आप डेटा को छोटे विखंडू में काटते हैं, तो आप इंजन को पहले क्लीनअप करने में सक्षम बनाते हैं - GC kicks in।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.