एक tupleअजगर में कम स्मृति अंतरिक्ष ले जाता है:
>>> a = (1,2,3)
>>> a.__sizeof__()
48
जबकि listस्मृति स्थान अधिक लगता है:
>>> b = [1,2,3]
>>> b.__sizeof__()
64
आंतरिक रूप से पायथन मेमोरी प्रबंधन पर क्या होता है?
एक tupleअजगर में कम स्मृति अंतरिक्ष ले जाता है:
>>> a = (1,2,3)
>>> a.__sizeof__()
48
जबकि listस्मृति स्थान अधिक लगता है:
>>> b = [1,2,3]
>>> b.__sizeof__()
64
आंतरिक रूप से पायथन मेमोरी प्रबंधन पर क्या होता है?
जवाबों:
मुझे लगता है कि आप सीपीथॉन का उपयोग कर रहे हैं और 64 बिट्स के साथ (मुझे अपने सीपीथॉन 2.7 64-बिट पर समान परिणाम मिला है)। अन्य पायथन कार्यान्वयन में अंतर हो सकता है या यदि आपके पास 32 बिट पायथन है।
कार्यान्वयन के बावजूद, lists परिवर्तनशील आकार के होते हैं जबकि tuples निश्चित आकार के होते हैं।
तो tupleएस सीधे तत्वों को संरचना के अंदर संग्रहीत कर सकता है, दूसरी ओर सूचियों को अप्रत्यक्ष की एक परत की आवश्यकता होती है (यह तत्वों के लिए एक संकेतक संग्रहीत करता है)। अप्रत्यक्ष की यह परत एक पॉइंटर है, 64 बिट सिस्टम पर, जो 64 बिट है, इसलिए 8bytes है।
लेकिन एक और बात यह है कि listवे करते हैं: वे अधिक-आवंटित करते हैं। अन्यथा हमेशाlist.append एक O(n)ऑपरेशन होगा - इसे परिशोधन करने के लिए (बहुत तेज !!!) इसे ओवर-आवंटित किया जाता है। लेकिन अब उसे आबंटित आकार और भरे हुए आकार पर नज़र रखनी होगी ( केवल एक आकार को संग्रहीत करने की आवश्यकता है, क्योंकि आवंटित और भरा हुआ आकार हमेशा समान है)। इसका मतलब है कि प्रत्येक सूची में एक और "आकार" संग्रहीत करना है जो 64 बिट सिस्टम पर एक 64 बिट पूर्णांक है, फिर से 8 बाइट्स।O(1)tuple
इसलिए lists की तुलना में कम से कम 16 बाइट्स अधिक मेमोरी की आवश्यकता होती है tuple। मैंने "कम से कम" क्यों कहा? अति-आवंटन के कारण। ओवर-आवंटन का मतलब है कि यह जरूरत से ज्यादा जगह आवंटित करता है। हालाँकि, ओवर-एलोकेशन की राशि "कैसे" पर निर्भर करती है कि आप सूची बनाएं और अपेंड / डिलीट हिस्ट्री:
>>> l = [1,2,3]
>>> l.__sizeof__()
64
>>> l.append(4) # triggers re-allocation (with over-allocation), because the original list is full
>>> l.__sizeof__()
96
>>> l = []
>>> l.__sizeof__()
40
>>> l.append(1) # re-allocation with over-allocation
>>> l.__sizeof__()
72
>>> l.append(2) # no re-alloc
>>> l.append(3) # no re-alloc
>>> l.__sizeof__()
72
>>> l.append(4) # still has room, so no over-allocation needed (yet)
>>> l.__sizeof__()
72
मैंने ऊपर स्पष्टीकरण के साथ कुछ चित्र बनाने का निर्णय लिया। शायद ये मददगार हों
यह है कि यह (योजनाबद्ध) आपके उदाहरण में स्मृति में संग्रहीत है। मैंने लाल (फ्री-हैंड) चक्रों के साथ अंतर पर प्रकाश डाला:
यह वास्तव में सिर्फ एक सन्निकटन है क्योंकि intऑब्जेक्ट्स भी पायथन ऑब्जेक्ट हैं और सीपीथॉन यहां तक कि छोटे पूर्णांक का भी पुन: उपयोग करता है, इसलिए स्मृति में वस्तुओं का संभवतः अधिक सटीक प्रतिनिधित्व (हालांकि पठनीय नहीं है) होगा:
उपयोगी कड़ियाँ:
tuple पाइथन 2.7 के लिए सीपीथॉन भंडार में संरचनाlist पाइथन 2.7 के लिए सीपीथॉन भंडार में संरचनाint पाइथन 2.7 के लिए सीपीथॉन भंडार में संरचनाध्यान दें कि __sizeof__वास्तव में "सही" आकार वापस नहीं करता है! यह केवल संग्रहीत मूल्यों का आकार लौटाता है। हालाँकि जब आप sys.getsizeofपरिणाम का उपयोग करते हैं तो वह अलग होता है:
>>> import sys
>>> l = [1,2,3]
>>> t = (1, 2, 3)
>>> sys.getsizeof(l)
88
>>> sys.getsizeof(t)
72
24 "अतिरिक्त" बाइट्स हैं। ये वास्तविक हैं , यह कचरा कलेक्टर ओवरहेड है जिसका __sizeof__विधि में हिसाब नहीं है । ऐसा इसलिए है क्योंकि आप आमतौर पर सीधे तौर पर जादू के तरीकों का उपयोग नहीं करते हैं - उन कार्यों का उपयोग करें जो जानते हैं कि उन्हें कैसे संभालना है, इस मामले में: sys.getsizeof(जो वास्तव में जीसी ओवरहेड को मूल्य से वापस लौटाता है __sizeof__)।
list()किसी सूची बोध का उपयोग करते समय अधिक आवंटन की मात्रा के बारे में जानना चाहते हैं ।
मैं CPython कोडबेस में एक गहरा गोता लगाऊंगा ताकि हम देख सकें कि वास्तव में आकारों की गणना कैसे की जाती है। आपके विशिष्ट उदाहरण में , कोई अति-आवंटन नहीं किया गया है, इसलिए मैं उस पर स्पर्श नहीं करूंगा ।
मैं यहाँ 64-बिट मान का उपयोग करने जा रहा हूँ, जैसे आप हैं।
listएस के लिए आकार की गणना निम्न फ़ंक्शन से की जाती है 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)एक मैक्रो है जो (लौटने ) ob_typeकी पकड़ लेता है जबकि एक अन्य मैक्रो है जो उस प्रकार से पकड़ लेता है। के रूप में गणना की जाती है जहां उदाहरण struct है।selfPyList_Type_PyObject_SIZEtp_basicsizetp_basicsizesizeof(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_typeob_size24
तो अभी के लिए 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) 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रों वास्तव में गणना निम्न प्रकार है:
sizeof(PyTupleObject) - sizeof(PyObject *)
क्यों एक अतिरिक्त 8बाइट हटा दिया जाता tp_basicsizeहै कुछ ऐसा है जिसे मैं पता नहीं लगा पाया हूं। (संभावित विवरण के लिए MSeifert की टिप्पणी देखें)
लेकिन, यह मूल रूप से आपके विशिष्ट उदाहरण में अंतर है । lists भी आवंटित किए गए तत्वों की एक संख्या के आसपास रखता है जो यह निर्धारित करने में मदद करता है कि फिर से कब आवंटित किया जाए।
अब, जब अतिरिक्त तत्व जोड़े जाते हैं, तो सूचियाँ वास्तव में O (1) प्राप्त करने के लिए इस ओवर-आवंटन का प्रदर्शन करती हैं। इससे MSIifert के उत्तर में बड़े आकार के परिणाम मिलते हैं।
ob_item[1]कि ज्यादातर एक प्लेसहोल्डर है (इसलिए यह समझ में आता है कि यह मूल से घटाया गया है)। का tupleउपयोग कर आवंटित किया गया है PyObject_NewVar। मैंने विवरण का पता नहीं लगाया है, इसलिए यह केवल एक शिक्षित अनुमान है ...
MSeifert जवाब इसे मोटे तौर पर कवर करता है; इसे सरल रखने के लिए आप सोच सकते हैं:
tupleअपरिवर्तनीय है। एक बार सेट होने के बाद, आप इसे बदल नहीं सकते। तो आप पहले से जानते हैं कि आपको उस ऑब्जेक्ट के लिए कितनी मेमोरी आवंटित करने की आवश्यकता है।
listपारस्परिक है। आप इसमें या इससे आइटम जोड़ या हटा सकते हैं। इसे इसका आकार (आंतरिक निहितार्थ के लिए) जानना होगा। यह आवश्यकतानुसार आकार देता है।
कोई मुफ्त भोजन नहीं है - ये क्षमताएं लागत के साथ आती हैं। इसलिए सूचियों के लिए स्मृति में ओवरहेड।
टपल का आकार उपसर्ग है, जिसका अर्थ है ट्यूल इनिशियलाइज़ेशन में इंटरप्रेटर निहित डेटा के लिए पर्याप्त जगह आवंटित करता है, और इसका अंत यह है, यह अपरिवर्तनीय (संशोधित नहीं किया जा सकता है), जबकि एक सूची एक उत्परिवर्ती वस्तु है इसलिए गतिशील गतिशील स्मृति का आबंटन, इसलिए प्रत्येक बार जब आप सूची को जोड़ते या संशोधित करते हैं तो स्थान आवंटित करने से बचें (बदले हुए डेटा को रखने के लिए पर्याप्त स्थान आवंटित करें और उस पर डेटा कॉपी करें), यह भविष्य के परिशिष्ट, संशोधनों, ... के लिए अतिरिक्त स्थान आवंटित करता है। इसे जोड़े।