c# - मेरे सरणी की सरणी इतनी मेमोरी क्यों लेती है?



.net memory-management (1)

मेरे परीक्षणों के आधार पर, मैं अनुमान लगा रहा हूं कि माइक्रो फ्रेमवर्क में वैल्यू टाइप्स वास्तविक मूल्य प्रकार नहीं हैं क्योंकि हम डेस्कटॉप सीएलआर पर उपयोग किए जाते हैं। कम से कम, वे बॉक्सिंग किया जा रहा है। और वहां एक और स्तर का संकेत भी शामिल हो सकता है। इन लागतों को एक एम्बेडेड प्लेटफ़ॉर्म के लिए काफी महत्वपूर्ण (मेमोरी ओवरहेड) में किया जाता है।

मैं अपने FixedSizedQueue में एक int[] में परिवर्तित हो FixedSizedQueue

असल में, मैंने UInt32[] का उपयोग करके समाप्त कर दिया और बिट UInt32[] चारों ओर लपेटने के लिए कुछ एक्सटेंशन विधियों को जोड़ा।

मैंने सोर्स कोड में थोड़ा सा पोक किया, लेकिन कुछ भी उपयोगी नहीं मिला (और मुझे सच में नहीं पता कि क्या देखना है)।

प्रश्न: माइक्रो फ्रेमवर्क कैसे structs की सरणी के लिए स्मृति आवंटित करता है?

दोहराने के लिए कोड के साथ बिटबकेट भंडार

संदर्भ और विस्तार

मैं एक यूएसबी कीबोर्ड से कीस्ट्रोक प्रोसेसिंग में देरी डालने के लिए एक निश्चित आकार के सरणी का उपयोग कर कतार बना रहा हूं। मैं कुंजी ऊपर और नीचे घटनाओं और देरी का प्रतिनिधित्व करने के लिए एक struct का उपयोग कर रहा हूँ।

public struct QueuedEvent
{
    public readonly EventType Type;        // Byte
    public readonly byte KeyPressed;
    public readonly TinyTimeSpan Delay;    // Int16

    public readonly static QueuedEvent Empty = new QueuedEvent();
}

public enum EventType : byte
{
    None = 0,
    Delay = 1,
    KeyDown = 2,
    KeyUp = 3,
    KeyPress = 4,
}

public class FixedSizeQueue
{
    private readonly QueuedEvent[] _Array;
    private int _Head = 0;
    private int _Tail = 0;

    public FixedSizeQueue(int size)
    {
        _Array = new QueuedEvent[size];
    }

    // Enqueue and Dequeue methods follow.
}

मैंने सोचा होगा कि मेरा QueuedEvent स्मृति में 4 बाइट्स पर कब्जा करेगा, लेकिन, कचरा कलेक्टर (विशेष रूप से VALUETYPE और SZARRAY प्रकार) के डीबग आउटपुट को देखने के आधार पर यह वास्तव में 84 बाइट्स ले रहा है! यह मुझे overkill के रूप में हमला करता है! (और यह वास्तव में 84 बाइट्स प्रत्येक प्रतीत होता है, क्योंकि मुझे आउटऑफमेमरी अपवाद मिलता है अगर मैं उनमें से 512 आवंटित करता हूं। मेरे पास ~ 20kb रैम उपलब्ध है, इसलिए मुझे आसानी से 512 आवंटित करने में सक्षम होना चाहिए)।

प्रश्न (दोबारा): माइक्रो फ्रेमवर्क एक संरचना के लिए 84 बाइट आवंटित करने का प्रबंधन कैसे करता है जो 4 में फिट हो सकता है?

कचरा कलेक्टर आंकड़े

QueuedEvent के विभिन्न आकार के सरणी की एक तालिका यहां दी QueuedEvent (जब मैं 0 आवंटित करता हूं तो रकम घटाता हूं):

+--------+-----------+-----------+---------+------------+-------+
| Number | VALUETYPE | B/Q'dEvnt | SZARRAY | B/Q'edEvnt | Total |
| 16     | 1152      | 72        | 192     | 12         | 84    |
| 32     | 2304      | 72        | 384     | 12         | 84    |
| 64     | 4608      | 72        | 768     | 12         | 84    |
| 128    | 9216      | 72        | 1536    | 12         | 84    |
+--------+-----------+-----------+---------+------------+-------+

SZARRAY संख्याओं के आधार पर, मुझे लगता है कि मेरे QueuedEvent फ़ील्ड को Int32 सीमाओं के साथ गठबंधन किया जा रहा है, इस प्रकार 12 बाइट्स ले जा रहे हैं। लेकिन मुझे नहीं पता कि अतिरिक्त 72 बाइट कहाँ से आते हैं।

संपादित करें: मुझे Debug.GC(true) को कॉल Debug.GC(true) और मेरे डीबगर आउटपुट में मिलने वाले डंप को देखकर इन नंबरों को प्राप्त कर रहा है। मुझे एक संदर्भ नहीं मिला है जो वास्तव में पहचानता है कि प्रत्येक संख्या का क्या अर्थ है।

मुझे एहसास है कि मैं बस एक int[] आवंटित कर सकता हूं, लेकिन इसका मतलब है कि मैं अच्छा encapsulation और संरचना की किसी भी प्रकार की सुरक्षा खो देता है। और मैं वास्तव में जानना चाहता हूं कि सूक्ष्म ढांचे में संरचना की वास्तविक लागत क्या है।

मेरा TinyTimeSpan नियमित TinyTimeSpan की तरह है, सिवाय इसके कि Int16 का प्रतिनिधित्व करने वाले Int16 की तुलना में कई मिलीसेकंड का प्रतिनिधित्व करने के लिए Int16 का उपयोग कर Int16 है।

public struct TinyTimeSpan
{
    public static readonly TinyTimeSpan Zero = new TinyTimeSpan(0);
    private short _Milliseconds;

    public TinyTimeSpan(short milliseconds)
    {
        _Milliseconds = milliseconds;
    }
    public TinyTimeSpan(TimeSpan ts)
    {
        _Milliseconds = (short)(ts.Ticks / TimeSpan.TicksPerMillisecond);
    }

    public int Milliseconds { get { return _Milliseconds; } }
    public int Seconds { get { return _Milliseconds * 1000; } }
}

मैं हार्डवेयर के रूप में एक FEZ डोमिनोज़ का उपयोग कर रहा हूँ। यह पूरी तरह से संभव है यह हार्डवेयर विशिष्ट है। इसके अलावा, माइक्रो फ्रेमवर्क 4.1।

संपादित करें - अधिक परीक्षण और टिप्पणी जवाब

मैंने पूरे समूह को और अधिक परीक्षण चलाया (इस समय एमुलेटर में, असली हार्डवेयर पर नहीं, लेकिन QueuedEvent लिए QueuedEvent समान हैं, इसलिए मुझे लगता है कि मेरा हार्डवेयर अन्य परीक्षणों के लिए समान होगा)।

दोहराने के लिए कोड के साथ बिटबकेट भंडार

निम्नलिखित अभिन्न प्रकार और structs VALUETYPE रूप में किसी भी ओवरहेड को आकर्षित नहीं करते हैं:

  • बाइट (1 बाइट)
  • Int32 (4 बाइट्स)
  • Int16 (2 बाइट्स)
  • Int64 (8 बाइट्स)
  • डबल (8 बाइट्स)
  • टाइमस्पैन (12 बाइट्स - अजीब, क्योंकि इसके आंतरिक सदस्य एक इंट 64 है)
  • डेटटाइम (12 बाइट्स - अजीब)

हालांकि, Guid करता है: प्रत्येक 36 बाइट्स का उपयोग करता है।

खाली स्थैतिक सदस्य 72 बाइट्स (एक सरणी में एक ही संरचना से कम 12 बाइट्स) का उपयोग करके VALUETYPE आवंटित करता है।

एक static सदस्य के रूप में सरणी आवंटित करना कुछ भी नहीं बदलता है।

डीबग या रिलीज मोड में चलना कोई फर्क नहीं पड़ता। मुझे नहीं पता कि जीसी डीबग जानकारी को बिना किसी डिबगर के कैसे प्राप्त किया जाए। लेकिन माइक्रो फ्रेमवर्क का अर्थ है, इसलिए मुझे नहीं पता कि एक गैर-संलग्न डीबगर का क्या असर होगा।

माइक्रो फ्रेमवर्क unsafe कोड का समर्थन नहीं करता है। न ही यह StructLayout समर्थन करता है (अच्छी तरह से, तकनीकी रूप से यह करता है, लेकिन कोई FieldOffset विशेषता नहीं है)। StructLayout Auto और Sequential कोई फर्क नहीं पड़ता।

यहां कुछ और structs और उनकी मापा स्मृति आवंटन हैं:

// Uses 12 bytes in SZARRAY and 24 in VALUETYPE, total = 36 each
public struct JustAnInt32
{
    public readonly Int32 Value;
}


// Uses 12 bytes in SZARRAY and 48 in VALUETYPE, total = 60 each
// Same as original QueuedEvent but only uses integral types.
public struct QueuedEventSimple
{
    public readonly byte Type;
    public readonly byte KeyPressed;
    public readonly short DelayMilliseconds;
    // Replacing the short with TimeSpan does not change memory usage.
}

// Uses 12 bytes in SZARRAY and 12 in VALUETYPE, total = 24 each
// I have to admit 24 bytes is a bit much for an empty struct!!
public struct Empty
{ 
}

ऐसा लगता है कि हर बार जब मैं एक कस्टम स्ट्रक्चर का उपयोग करता हूं, तो मुझे कुछ प्रकार का ओवरहेड होता है। और इससे कोई फर्क नहीं पड़ता कि मैं संरचना में क्या शामिल करता हूं, इसे हमेशा SZARRAY में 12 बाइट की आवश्यकता होती है। तो मैंने यह कोशिश की:

// Uses 12 bytes in SZARRAY and 36 in VALUETYPE, total = 48 each
public struct DifferentEntity
{
    public readonly Double D;
    public readonly TimeSpan T;
}

// Uses 12 bytes in SZARRAY and 108 in VALUETYPE, total = 120 each
public struct MultipleEntities
{
    public readonly DifferentEntity E1;
    public readonly DifferentEntity E2;
}

// Uses 12 bytes in SZARRAY and 60 in VALUETYPE, total = 72 each
// This is equivalent to MultipleEntities, but has quite different memory usage.
public struct TwoDoublesAndTimeSpans
{
    public readonly double D1;
    public readonly TimeSpan T1;
    public readonly double D2;
    public readonly TimeSpan T2;
}

मामूली संपादन

अपना खुद का जवाब पोस्ट करने के बाद, मुझे एहसास हुआ कि प्रति आइटम SZARRAY में हमेशा 12 बाइट ओवरहेड था। तो मैंने एक object[] परीक्षण किया object[] । संदर्भ प्रकार माइक्रो फ्रेमवर्क में प्रत्येक 12 बाइट्स का उपभोग करते हैं।

एक खाली संरचना public struct Empty { } प्रत्येक 24 बाइट खपत करता है।





.net-micro-framework