2013-03-31 9 views
10

मैं सी # में द्रव सिमुलेशन पर काम कर रहा हूं। प्रत्येक चक्र में मुझे अंतरिक्ष में अलग-अलग बिंदुओं पर द्रव की वेग की गणना करने की आवश्यकता होती है। उस गणना के हिस्से के रूप में, मुझे कुछ डबल [] सरणी रखने के लिए स्क्रैच स्पेस के लिए कुछ दस किलो किलोबाइट की आवश्यकता होती है (सरणी का सटीक आकार कुछ इनपुट डेटा पर निर्भर करता है)। सरणी केवल उन पद्धतियों की अवधि के लिए आवश्यक होती हैं जो उनका उपयोग करती हैं, और कुछ अलग-अलग विधियां हैं जिन्हें इस तरह स्क्रैच स्पेस की आवश्यकता होती है।किसी प्रबंधित वातावरण में स्क्रैच मेमोरी

मैं इसे देखना, वहाँ खरोंच सरणियों के निर्माण के लिए कुछ अलग समाधान कर रहे हैं:

  1. उपयोग 'नए' हर बार विधि कहा जाता है ढेर से स्मृति हड़पने के लिए। यह वही है जो मैं पहले कर रहा था, हालांकि यह कचरा कलेक्टर पर बहुत अधिक दबाव डालता है, और कुछ एमएस स्पाइक्स एक या दो बार एक दूसरे से वास्तव में परेशान हैं।

  2. विधियों को कॉल करते समय स्क्रैच सरणी पैरामीटर के रूप में पास किया गया है। समस्या यह है कि यह उपयोगकर्ता को उन्हें प्रबंधित करने के लिए मजबूर करता है, जिसमें उचित रूप से उनका आकार शामिल है, जो एक बड़ा दर्द है। और यह एपीआई को बदलने के बाद से कम या ज्यादा स्क्रैच मेमोरी का उपयोग करना मुश्किल बनाता है।

  3. प्रोग्राम स्टैक से स्क्रैच मेमोरी आवंटित करने के लिए एक असुरक्षित संदर्भ में स्टैकलॉक का उपयोग करें। यह ठीक काम करेगा, सिवाय इसके कि मुझे अपने कोड में असुरक्षित ब्लॉक को असुरक्षित और असुरक्षित करने की आवश्यकता होगी, जिसे मैं टालना चाहता हूं।

  4. प्रोग्राम शुरू होने पर निजी एरे को प्रीलाकेट करें। यह ठीक है सिवाय इसके कि मुझे जरूरी सरणी के आकार को जरूरी नहीं है जब तक कि मैं कुछ इनपुट डेटा देख सकूं। और यह वास्तव में गन्दा हो जाता है क्योंकि आप इन निजी चर के दायरे को केवल एक ही विधि तक सीमित नहीं कर सकते हैं, इसलिए वे लगातार नामस्थान को प्रदूषित कर रहे हैं। और यह खराब तरीके से स्केल करता है क्योंकि स्क्रैच मेमोरी की आवश्यकता वाले तरीकों की संख्या बढ़ जाती है, क्योंकि मैं बहुत सारी मेमोरी आवंटित कर रहा हूं जो केवल उस समय का एक अंश उपयोग करता है।

  5. किसी प्रकार का केंद्रीय पूल बनाएं, और पूल से स्क्रैच मेमोरी सरणी आवंटित करें। इसके साथ मुख्य समस्या यह है कि मुझे केंद्रीय पूल से गतिशील आकार के सरणी आवंटित करने का एक आसान तरीका नहीं दिखता है। मैं एक प्रारंभिक ऑफ़सेट और लंबाई का उपयोग कर सकता हूं और सभी स्क्रैच मेमोरी अनिवार्य रूप से एक बड़ी सरणी साझा कर सकती हैं, लेकिन मेरे पास बहुत सारे मौजूदा कोड हैं जो डबल [] एस मानते हैं। और मुझे इस तरह के पूल थ्रेड को सुरक्षित रखने के लिए सावधान रहना होगा।

...

किसी को भी एक ऐसी ही समस्या के साथ किसी भी अनुभव है? अनुभव से पेशकश करने के लिए कोई सलाह/सबक?

using(double[] scratchArray = new double[buffer]) 
{ 
    // Code here... 
} 

इस बयान का उपयोग कर के अंत में descructor फोन करके स्मृति स्पष्ट रूप से मुक्त कर देगा:

+0

क्या आपका वास्तव में कुछ किलो किलोबाइट का मतलब था? क्योंकि यह इतनी छोटी राशि है, मैं इसके लिए मेमोरी प्रबंधन के बारे में चिंता नहीं करता ... –

+0

यह बहुत कुछ नहीं लगता है, लेकिन अगर मैं 2000 चक्र/सेकंड चला रहा हूं, अचानक यह 60 एमबी/सेकंड की तरह है, और जीसी नोटिस शुरू होता है। –

+0

@JayLemmon, मुझे लगता है कि आप प्रदर्शन विवरणों के लिए इन विवरणों की देखभाल कर रहे हैं, है ना? यदि आपकी परियोजना समाप्त नहीं हुई है, तो मेरा सुझाव है कि आप इसे समाप्त होने तक प्रदर्शन की परवाह नहीं करते हैं। [Premature Optimization] (http://c2.com/cgi/wiki?PrematureOptimization) के बारे में इस आलेख को देखें। यदि परियोजना समाप्त हो गई है, तो लेख सामान्य रूप से __optimization__ पर भी दिलचस्प अवलोकन देता है। मैंने एक हिस्सा उद्धृत किया: "एक आम गलतफहमी यह है कि अनुकूलित कोड आवश्यक रूप से अधिक जटिल है [...] बेहतर फैक्टर कोड अक्सर तेज़ी से चलता है और कम स्मृति का भी उपयोग करता है [...]"। – jay

उत्तर

3

आप कोड का उपयोग करता है शोध करे इस तरह की एक का उपयोग कर बयान में सरणियों खरोंच लपेट कर सकते हैं।

दुर्भाग्य से ऐसा लगता है कि उपरोक्त सत्य नहीं है! इसके बदले, आप एक सहायक कार्य करने के साथ कुछ कोशिश कर सकते हैं जो उचित आकार (आकार से 2 से अधिक की निकटतम शक्ति) की एक सरणी देता है, और यदि यह अस्तित्व में नहीं है, तो इसे बनाते हैं। इस तरह, आपके पास केवल सरणी की लॉगरिदमिक संख्या होगी। यदि आप चाहते थे कि यह थ्रेड-सुरक्षित हो, हालांकि आपको थोड़ा और परेशानी होगी।

यह कुछ इस तरह दिखाई दे सकता है: (Algorithm for finding the smallest power of two that's greater or equal to a given value से pow2roundup का उपयोग कर)

private static Dictionary<int,double[]> scratchArrays = new Dictionary<int,double[]>(); 
/// Round up to next higher power of 2 (return x if it's already a power of 2). 
public static int Pow2RoundUp (int x) 
{ 
    if (x < 0) 
     return 0; 
    --x; 
    x |= x >> 1; 
    x |= x >> 2; 
    x |= x >> 4; 
    x |= x >> 8; 
    x |= x >> 16; 
    return x+1; 
} 
private static double[] GetScratchArray(int size) 
{ 
    int pow2 = Pow2RoundUp(size); 
    if (!scratchArrays.ContainsKey(pow2)) 
    { 
     scratchArrays.Add(pow2, new double[pow2]); 
    } 
    return scratchArrays[pow2]; 
} 

संपादित करें: थ्रेड के लिए सुरक्षित संस्करण: यह अभी भी चीजें हैं जो कचरा एकत्र करने के लिए है, लेकिन यह धागा विशिष्ट होने वाला है और बहुत कम ओवरहेड होना चाहिए।

[ThreadStatic] 
private static Dictionary<int,double[]> _scratchArrays; 

private static Dictionary<int,double[]> scratchArrays 
{ 
    get 
    { 
     if (_scratchArrays == null) 
     { 
      _scratchArrays = new Dictionary<int,double[]>(); 
     } 
     return _scratchArrays; 
    } 
} 

/// Round up to next higher power of 2 (return x if it's already a power of 2). 
public static int Pow2RoundUp (int x) 
{ 
    if (x < 0) 
     return 0; 
    --x; 
    x |= x >> 1; 
    x |= x >> 2; 
    x |= x >> 4; 
    x |= x >> 8; 
    x |= x >> 16; 
    return x+1; 
} 
private static double[] GetScratchArray(int size) 
{ 
    int pow2 = Pow2RoundUp(size); 
    if (!scratchArrays.ContainsKey(pow2)) 
    { 
     scratchArrays.Add(pow2, new double[pow2]); 
    } 
    return scratchArrays[pow2]; 
} 
+0

दुर्भाग्य से, विस्थापित! = एकत्रित :(http://stackoverflow.com/questions/655902/using-and-garbage-collection –

+0

देखें दुर्भाग्यपूर्ण है। मैं अपना उत्तर किसी अन्य विचार से अपडेट करूंगा। –

+0

आह, मैंने नहीं किया ' टी थ्रेडस्टैटिक के बारे में पता नहीं है। इससे थ्रेड-लोकल-आईश स्क्रैच मेमोरी बहुत आसान हो जाती है। –

7

मैं आपकी स्थिति से सहानुभूति व्यक्त करता हूं; जब मैं रोज़लिन पर काम कर रहा था, हमने अस्थायी कामकाजी सरणी आवंटित करने से संग्रह दबाव के कारण संभावित प्रदर्शन समस्याओं को बहुत सावधानी से माना। जिस समाधान पर हम बस गए थे वह एक पूलिंग रणनीति थी।

एक कंपाइलर में सरणी आकार छोटे होते हैं और इसलिए बार-बार दोहराया जाता है। आपकी स्थिति में, यदि आपके पास बड़े सरणी हैं तो मैं क्या करूँगा टॉम के सुझाव का पालन करें: प्रबंधन की समस्या को सरल बनाएं और कुछ जगह बर्बाद करें। जब आप आकार x की सरणी के लिए पूल से पूछते हैं, तो गोल x को दो की नजदीक शक्ति तक बढ़ाएं और उस आकार की सरणी आवंटित करें, या पूल से एक ले जाएं। कॉलर को एक सरणी मिलती है जो थोड़ा बड़ा होता है, लेकिन उससे निपटने के लिए उन्हें लिखा जा सकता है। उचित आकार की सरणी के लिए पूल को खोजना बहुत मुश्किल नहीं होना चाहिए। या आप पूल का एक गुच्छा बनाए रख सकते हैं, आकार 1024 के सरणी के लिए एक पूल, 2048 के लिए एक, और इसी तरह।

थ्रेडसेफ पूल लिखना बहुत कठिन नहीं है, या आप पूल थ्रेड स्थिर बना सकते हैं और प्रति थ्रेड एक पूल कर सकते हैं।

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

एक और तरीका सरणी के चारों ओर एक मुखौटा रैपर लिखना है, इसे आईडीस्पोज़ेबल को कार्यान्वित करें ताकि आप "उपयोग" (*) का उपयोग कर सकें, और उस पर एक फाइनलाइज़र बना सकें जो ऑब्जेक्ट को पूल में वापस रखता है, इसे पुनरुत्थित करता है। (सुनिश्चित करें कि फाइनलइज़र "मुझे अंतिम रूप देने की आवश्यकता है" पर वापस आना सुनिश्चित करें।) पुनरुत्थान करने वाले फाइनलर्स मुझे परेशान करते हैं; मैं व्यक्तिगत रूप से पूर्व दृष्टिकोण पसंद करता हूं, जो हमने रोज़लिन में किया था।


(*) हाँ, यह सिद्धांत है कि "का उपयोग" का संकेत देना चाहिए कि एक अप्रबंधित संसाधन ऑपरेटिंग सिस्टम को लौट जा रहा है उल्लंघन करती है। अनिवार्य रूप से हम प्रबंधित प्रबंधन को अपने स्वयं के प्रबंधन करके एक अप्रबंधित संसाधन के रूप में देख रहे हैं, इसलिए यह इतना बुरा नहीं है।

+0

धन्यवाद, यह बिल्कुल वही युद्ध कहानी है जिसकी मैं उम्मीद कर रहा था :) एक पूल अब तक का सबसे उचित समाधान की तरह लग रहा है, और मैं नहीं करता इस तरह की स्मृति बर्बाद नहीं करते हैं। मैं संसाधनों को वापस छोड़ने के लिए उपयोगकर्ताओं पर भरोसा करने के लिए अभी भी बेचा नहीं गया हूं, या इसे किसी निपटान विधि में क्रैक करने की कोशिश कर रहा हूं, लेकिन मुझे लगता है कि यह वही है। –

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