2013-05-09 5 views
5

मेरे पास एक ऐप है जिसमें मैं बाइट्स का एक बहुत बड़ा "घन" बनाने की कोशिश कर रहा हूं। एक 3 आयामी सरणी (~ 1000x1000x500) उन सभी मूल्यों को सहेजती है जिनमें मुझे रूचि है - लेकिन मुझे स्मृति समस्याओं से बाहर निकलना है। हालांकि इसकी अपेक्षा की गई थी, मुझे प्राप्त होने वाले विभिन्न ओओएम संदेशों का BEHAVIOR काफी भ्रमित कर रहा है। पहली:मेमोरी भ्रम

Foo[,,] foo1 = new Foo[1000, 1000, 500];

एक OOM त्रुटि के साथ विफल, लेकिन इस नहीं करता है:
Foo[,,] foo1 = new Foo[250, 1000, 500];
Foo[,,] foo2 = new Foo[250, 1000, 500];
Foo[,,] foo3 = new Foo[250, 1000, 500];
Foo[,,] foo4 = new Foo[250, 1000, 500];

कोड के इन दो सेटों अनिवार्य रूप से स्मृति का एक ही राशि का उपभोग नहीं करना चाहिए?

इसके अलावा, मुझे मूल रूप से त्रुटि संदेश प्राप्त हो रहा था जब ~ 1.5 जीबी का उपभोग किया गया था, लेकिन मुझे लगता है कि इसे 64 बिट एप्लिकेशन में स्विच करके मुझे विफल होने से पहले बहुत अधिक स्मृति का उपयोग करने देगी।

क्या मैं स्टैक स्पेस सीमाओं में भाग रहा हूं? और यदि हां, तो मैं ढेर पर पूरी तरह से मौजूद होने के बिना इस ढांचे पर पूरी तरह से ढेर पर कैसे स्थापित कर सकता हूं?

अग्रिम धन्यवाद - मैं किसी भी प्रकाश की प्रतीक्षा करता हूं कि कोई भी इस व्यवहार पर ध्यान दे सकता है।

+0

मुझे लगता है कि यदि आप 64x के लिए संकलित करते हैं तो आप ठीक होने जा रहे हैं। मुझे लगता है कि आप केवल पहले सरणी के साथ किसी ऑब्जेक्ट की सीमा पर जाते हैं। तो हाँ दो कथन मेमोरी में लगभग एक ही स्थान लेते हैं, लेकिन यह केवल इसलिए है क्योंकि आप .net की प्रति वस्तु (कम से कम 32x के लिए) पर चलते हैं –

+3

"नया" का उपयोग करके इसे ढेर पर रखा जाएगा। जब आप इसे विभाजित करते हैं तो यह शायद काम करता है क्योंकि अब आप 500 मिलियन _contiguous_ "Foo" आकार के टुकड़े का अनुरोध नहीं कर रहे हैं। –

+1

क्या आप वाकई वाकई सुनिश्चित हैं कि आपको 'फू' की प्री-आवंटित सरणी की आवश्यकता है।समझाएं कि आप उस आकार की सरणी का उपयोग करके क्या करने की कोशिश कर रहे हैं, इसे संभालने के बेहतर तरीके हो सकते हैं (शायद एक नेस्टेड डिक्शनरी जिसमें केवल आपके द्वारा आवश्यक आबादी वाले मूल्य शामिल हैं 'int >> ') –

उत्तर

3

संपादित

मैं अपने जवाब की एक पूरी तरह से विशेष रुप से कार्यान्वयन पर सोच रहा था, मैंने सोचा कि मैं संलग्न चाहते हैं। मुझे यकीन नहीं है कि समानांतरता मदद करेगी, शायद यह initializer पर निर्भर करती है।

using System; 
using System.Linq; 

public static T[][][] NewJagged<T>(
     int h, 
     int w, 
     ing d, 
     Func<int, int, int, T> initializer = null, 
     bool parallelize = true) 
{ 
    if (h < 1) 
    { 
     throw new ArgumentOutOfRangeException("h", h, "Dimension less than 1.") 
    } 

    if (w < 1) 
    { 
     throw new ArgumentOutOfRangeException("w", w, "Dimension less than 1.") 
    } 

    if (d < 1) 
    { 
     throw new ArgumentOutOfRangeException("d", d, "Dimension less than 1.") 
    } 

    if (initializer == null) 
    { 
     initializer = (i, j, k) => default(T); 
    } 

    if (parallelize) 
    { 
     return NewJaggedParalellImpl(h, w, d, initializer); 
    } 

    return NewJaggedImpl(h, w, d, initializer); 
} 

private static T[][][] NewJaggedImpl<T>(
     int h, 
     int w, 
     int d, 
     Func<int, int, int, T> initializer) 
{ 
    var result = new T[h][][]; 
    for (var i = 0; i < h; i++) 
    { 
     result[i] = new T[w][]; 
     for (var j = 0; j < w; j++) 
     { 
      result[i][j] = new T[d]; 
      for (var k = 0; k < d; k++) 
      { 
       result[i][j][k] = initializer(i, j, k); 
      } 
     } 
    } 

    return result; 
} 

private static T[][][] NewJaggedParalellImpl<T>(
     int h, 
     int w, 
     int d, 
     Func<int, int, int, T> initializer) 
{ 
    var result = new T[h][][]; 
    ParallelEnumerable.Range(0, h).ForAll(i => 
    { 
     result[i] = new T[w][]; 
     ParallelEnumerable.Range(0, w).ForAll(j => 
     { 
      result[i][j] = new T[d]; 
      ParallelEnumerable.Range(0, d).ForAll(k => 
      { 
       result[i][j][k] = initializer(i, j, k); 
      }); 
     }); 
    }); 

    return result; 
}    

यह समारोह पूरी तरह से सामान्य बनाता है, लेकिन अभी भी आप सरल वाक्य रचना छोड़ देता है,

var foo1 = NewJagged<Foo>(1000, 1000, 500); 

लेकिन आप कल्पना हो और आरंभीकरण पर समानांतर में पॉप्युलेट सकता है,

var foo2 = NewJagged<Foo>(
    1000, 
    1000, 
    5000, 
    (i, j, k) => 
     { 
      var pos = (i * 1000 * 500) + (j * 500) + k; 
      return ((pos % 2) == 0) ? new Foo() : null; 
     }); 
इस उदाहरण में

, एक चेकरबोर्ड प्रभाव के साथ populating (मुझे लगता है।);


यह शुरू में आपकी समस्या को जवाब देने के लिए प्रतीत नहीं कर सकते हैं ...

यदि आप एक समारोह, इस

public static T[][][] ThreeDimmer<T>(int h, int w, int d) where T : new() 
{ 
    var result = new T[h][][]; 
    for (var i = 0; i < h; i++) 
    { 
     result[i] = new T[w][]; 
     for (var j = 0; j < w; j++) 
     { 
      result[i][j] = new T[d]; 
      for (var k = 0; k < d; k++) 
      { 
       result[i][j][k] = new T(); 
      } 
     } 
    } 

    return result; 
} 

की तरह कुछ था तो फिर तुम एक 3 आयामी का आरंभीकरण समझाया है | संदर्भ प्रकार के jagged सरणी। यह आपको करने की अनुमति देगा,

Foo[][][] foo1 = ThreeDimmer<Foo>(1000, 1000, 500); 

यह बहुआयामी सरणी के स्मृति विखंडन के मुद्दों से बच जाएगा। यह other pitfalls and limitations से भी बच जाएगा, जिससे आप इसके बजाय एक तेज़ और अधिक लचीला जाली सरणी दे सकते हैं।

+0

वाह - यह एफएआर द्वारा किया गया सबसे उपयोगी पोस्ट मैंने कभी बनाया है। सचमुच हर एक प्रतिक्रिया बहुत उपयोगी रही है। 4.5 समाधान के उन्नयन के साथ संयुक्त इस समाधान ने मेरे सभी मुद्दों का समाधान किया है! सबको धन्यवाद! – eejai42

3

मेरे लिए, यह स्मृति विखंडन समस्या की तरह दिखता है। साथ ही, ध्यान दें कि new ढेर का उपयोग करता है।

पहले उदाहरण में आप स्मृति के बहुत बड़े हिस्से के लिए पूछ रहे हैं और यह संभव है कि ओएस, चाहे आपके सिस्टम में कितनी रैम है, हो सकता है कि स्मृति की एक संगत ब्लॉक नहीं मिल पाती ।

छोटे आवंटन काम करते हैं क्योंकि स्मृति के छोटे संगत ब्लॉक हमेशा बड़े से अधिक भरपूर होते हैं।

+0

नाइटपिकिंग: सी # 'नया 'में एक ढेर आवंटन के बराबर नहीं है। –

4

चीज है, जब आप एक सरणी आवंटित करते हैं, तो आपको संगत मेमोरी की आवश्यकता होती है।

100 एमबी के 1 विशाल ब्लॉक को खोजने के लिए प्रत्येक में 10 एमबी के आकार के साथ राम में संगत स्मृति के 10 ब्लॉक खोजने की अधिक संभावना है।

मान लीजिए कि आपके पास 0 से 99 के साथ रैम के 100 बाइट थे। यदि आपने आकार 23 पर आकार 1 बाइट के साथ स्मृति का एक ब्लॉक आवंटित किया है, उदाहरण के लिए, हालांकि आपके पास 99 बाइट रैम शेष है, अगर आप आवंटित करना चाहते हैं 99 बाइट्स के आकार के साथ स्मृति का एक ब्लॉक, आप असफल हो जाएंगे क्योंकि स्मृति संगत होना चाहिए। इस तरह के मामले में आवंटित करने वाला सबसे बड़ा ब्लॉक 76 बाइट लंबा होगा।

+0

ग्रेट स्पष्टीकरण भी। मैंने इस तथ्य के प्रभाव पर विचार नहीं किया था कि यह संगत होना चाहिए। धन्यवाद! – eejai42

4

एक 32 बिट ऐप 4 जीबी एड्रेस स्पेस तक सीमित है, इसलिए यह ऊपरी सीमा है। यदि प्रक्रिया 32 बिट ओएस पर चलती है, तो यह ऐप और ओएस के सेटअप के आधार पर 2 या 3 जीबी तक सीमित है।

पहले मामले में आप एक बड़ी सरणी आवंटित कर रहे हैं। .NET arrays में ढेर पर आवंटित किया जाता है, इसलिए स्टैक स्पेस यहां समस्या नहीं है। प्रश्न में संख्याओं को देखते हुए मुझे लगता है कि सरणी लगभग 1.5 जीबी है। यह संभालने के लिए कि सीएलआर को स्मृति के एक संगत ब्लॉक की आवश्यकता है। यदि आप छोटे टुकड़ों में समान बाइट्स आवंटित करते हैं (जैसा कि दूसरे उदाहरण में) रनटाइम आवश्यकतानुसार स्मृति आवंटित करने का एक बेहतर मौका खड़ा होगा।

फिर भी, कम से कम 2 जीबी उपलब्ध होने पर आपको लगता है कि 1.5 जीबी कोई समस्या नहीं होनी चाहिए, लेकिन तथ्य यह है कि एक प्रक्रिया पता स्थान का उपयोग एक छोर से दूसरी तरफ सख्ती से नहीं करती है। एक बार डीएलएल प्रक्रिया में लोड हो जाने के बाद पता स्थान खंडित हो जाता है।

मेरे अनुभव में 32 बिट प्रबंधित ऐप्स (32 बिट ओएस पर) आमतौर पर लगभग 1.5 जीबी ढेर स्पेस तक सीमित होते हैं, जो आप देख रहे ओओएम को समझाएंगे।

64 बिट ओएस पर एक ही ऐप चलाने से ऐप को पूरे 4 जीबी एड्रेस स्पेस तक पहुंच मिल जाएगी, जो प्रबंधित ढेर के लिए उपलब्ध जगह को प्रभावित करेगी।

ऐप को 64 बिट ऐप में बदलना, एड्रेस स्पेस आकार को 4 जीबी से 8 टीबी में बदल देता है। हालांकि, यहां तक ​​कि इस मामले में आपको यह ध्यान रखना चाहिए कि किसी भी .NET ऑब्जेक्ट का डिफ़ॉल्ट अधिकतम आकार 2 जीबी है। विवरण के लिए this question देखें।

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