python - सूचियों की तुलना में ट्यूपल्स मेमोरी में कम जगह क्यों लेते हैं?




python-2.7 list (3)

एक tuple पायथन में कम मेमोरी स्पेस लेता है:

>>> a = (1,2,3)
>>> a.__sizeof__()
48

जबकि list s में अधिक मेमोरी स्थान है:

>>> b = [1,2,3]
>>> b.__sizeof__()
64

आंतरिक रूप से पायथन मेमोरी प्रबंधन पर क्या होता है?


MSeifert जवाब इसे मोटे तौर पर कवर करता है; इसे सरल रखने के लिए आप सोच सकते हैं:

tuple अचल है। एक बार सेट होने के बाद, आप इसे बदल नहीं सकते। तो आप पहले से जानते हैं कि आपको उस ऑब्जेक्ट के लिए कितनी मेमोरी आवंटित करने की आवश्यकता है।

list परस्पर है। आप इसमें या इससे आइटम जोड़ या हटा सकते हैं। इसे इसका आकार (आंतरिक निहितार्थ के लिए) जानना होगा। यह आवश्यकतानुसार आकार देता है।

कोई मुफ्त भोजन नहीं है - ये क्षमताएं लागत के साथ आती हैं। इसलिए सूचियों के लिए स्मृति में ओवरहेड।


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


मैं CPython कोडबेस में एक गहरा गोता लगाऊंगा ताकि हम देख सकें कि वास्तव में आकारों की गणना कैसे की जाती है। आपके विशिष्ट उदाहरण में , कोई अति-आवंटन नहीं किया गया है, इसलिए मैं उस पर स्पर्श नहीं करूंगा

मैं यहाँ 64-बिट मानों का उपयोग करने जा रहा हूँ, जैसे आप हैं।

list s के लिए आकार की गणना निम्न फ़ंक्शन, list_sizeof :

static PyObject *
list_sizeof(PyListObject *self)
{
    Py_ssize_t res;

    res = _PyObject_SIZE(Py_TYPE(self)) + self->allocated * sizeof(void*);
    return PyInt_FromSsize_t(res);
}

यहां Py_TYPE(self) एक मैक्रो है जो self के ob_type ( PyList_Type लौटते PyList_Type ) को _PyObject_SIZE है जबकि _PyObject_SIZE एक अन्य मैक्रो है जो उस प्रकार से tp_basicsize पकड़ लेता है। tp_basicsize की गणना sizeof(PyListObject) रूप में की जाती है, जहां PyListObject उदाहरण संरचना है।

PyListObject संरचना में तीन क्षेत्र हैं:

PyObject_VAR_HEAD     # 24 bytes 
PyObject **ob_item;   #  8 bytes
Py_ssize_t allocated; #  8 bytes

इन टिप्पणियों (जो मैंने छंटनी की है) की व्याख्या करते हुए कि वे क्या हैं, उन्हें पढ़ने के लिए ऊपर दिए गए लिंक का पालन करें। PyObject_VAR_HEAD तीन 8 बाइट फ़ील्ड ( ob_refcount , ob_type और ob_size ) में फैलता है, इसलिए 24 बाइट योगदान देता है।

तो अब के लिए res है:

sizeof(PyListObject) + self->allocated * sizeof(void*)

या:

40 + self->allocated * sizeof(void*)

यदि सूची उदाहरण में ऐसे तत्व हैं जो आवंटित किए गए हैं। दूसरा भाग उनके योगदान की गणना करता है। self->allocated , जैसा कि नाम से ही स्पष्ट है, आवंटित तत्वों की संख्या रखता है।

किसी भी तत्व के बिना, सूचियों के आकार की गणना की जाती है:

>>> [].__sizeof__()
40

उदाहरण की संरचना का आकार।

tuple ऑब्जेक्ट्स tuple_sizeof फ़ंक्शन को परिभाषित नहीं करता है। इसके बजाय, वे अपने आकार की गणना करने के लिए object_sizeof का उपयोग करते हैं:

static PyObject *
object_sizeof(PyObject *self, PyObject *args)
{
    Py_ssize_t res, isize;

    res = 0;
    isize = self->ob_type->tp_itemsize;
    if (isize > 0)
        res = Py_SIZE(self) * isize;
    res += self->ob_type->tp_basicsize;

    return PyInt_FromSsize_t(res);
}

यह list एस के लिए के रूप में, tp_basicsize पकड़ लेता है और, यदि ऑब्जेक्ट में एक शून्य-शून्य tp_itemsize (जिसका अर्थ है कि चर-लंबाई के उदाहरण हैं), यह Py_SIZE में आइटमों की संख्या को गुणा करता है (जो इसे Py_SIZE माध्यम से Py_SIZE ) tp_itemsize साथ।

tp_basicsize फिर से sizeof(PyTupleObject) का उपयोग करता है जहां PyTupleObject संरचना में शामिल हैं :

PyObject_VAR_HEAD       # 24 bytes 
PyObject *ob_item[1];   # 8  bytes

तो, बिना किसी तत्व के (अर्थात, Py_SIZE रिटर्न 0 ) रिक्त ट्यूपल्स का आकार sizeof(PyTupleObject) बराबर है:

>>> ().__sizeof__()
24

है ना? ठीक है, यहाँ एक विषमता है, जिसके लिए मुझे कोई स्पष्टीकरण नहीं मिला, tp_basicsize का tuple s वास्तव में इस प्रकार है:

sizeof(PyTupleObject) - sizeof(PyObject *)

अतिरिक्त 8 बाइट्स को tp_basicsize से क्यों हटाया जाता है, यह मुझे पता नहीं चल पाया है। (संभावित स्पष्टीकरण के लिए MSeifert की टिप्पणी देखें)

लेकिन, यह मूल रूप से आपके विशिष्ट उदाहरण में अंतर है list एस भी आवंटित तत्वों की एक संख्या के आसपास रहती है जो यह निर्धारित करने में मदद करती है कि फिर से कब आवंटित किया जाए।

अब, जब अतिरिक्त तत्व जोड़े जाते हैं, तो सूचियाँ वास्तव में O (1) प्राप्त करने के लिए इस ओवर-आवंटन का प्रदर्शन करती हैं। इससे MSeifert के उत्तर में बड़े आकार के परिणाम मिलते हैं।






python-internals