एक 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 बिट पायथन है।
कार्यान्वयन के बावजूद, list
s परिवर्तनशील आकार के होते हैं जबकि tuple
s निश्चित आकार के होते हैं।
तो tuple
एस सीधे तत्वों को संरचना के अंदर संग्रहीत कर सकता है, दूसरी ओर सूचियों को अप्रत्यक्ष की एक परत की आवश्यकता होती है (यह तत्वों के लिए एक संकेतक संग्रहीत करता है)। अप्रत्यक्ष की यह परत एक पॉइंटर है, 64 बिट सिस्टम पर, जो 64 बिट है, इसलिए 8bytes है।
लेकिन एक और बात यह है कि list
वे करते हैं: वे अधिक-आवंटित करते हैं। अन्यथा हमेशाlist.append
एक O(n)
ऑपरेशन होगा - इसे परिशोधन करने के लिए (बहुत तेज !!!) इसे ओवर-आवंटित किया जाता है। लेकिन अब उसे आबंटित आकार और भरे हुए आकार पर नज़र रखनी होगी ( केवल एक आकार को संग्रहीत करने की आवश्यकता है, क्योंकि आवंटित और भरा हुआ आकार हमेशा समान है)। इसका मतलब है कि प्रत्येक सूची में एक और "आकार" संग्रहीत करना है जो 64 बिट सिस्टम पर एक 64 बिट पूर्णांक है, फिर से 8 बाइट्स।O(1)
tuple
इसलिए list
s की तुलना में कम से कम 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 है।self
PyList_Type
_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
) 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 की टिप्पणी देखें)
लेकिन, यह मूल रूप से आपके विशिष्ट उदाहरण में अंतर है । list
s भी आवंटित किए गए तत्वों की एक संख्या के आसपास रखता है जो यह निर्धारित करने में मदद करता है कि फिर से कब आवंटित किया जाए।
अब, जब अतिरिक्त तत्व जोड़े जाते हैं, तो सूचियाँ वास्तव में O (1) प्राप्त करने के लिए इस ओवर-आवंटन का प्रदर्शन करती हैं। इससे MSIifert के उत्तर में बड़े आकार के परिणाम मिलते हैं।
ob_item[1]
कि ज्यादातर एक प्लेसहोल्डर है (इसलिए यह समझ में आता है कि यह मूल से घटाया गया है)। का tuple
उपयोग कर आवंटित किया गया है PyObject_NewVar
। मैंने विवरण का पता नहीं लगाया है, इसलिए यह केवल एक शिक्षित अनुमान है ...
MSeifert जवाब इसे मोटे तौर पर कवर करता है; इसे सरल रखने के लिए आप सोच सकते हैं:
tuple
अपरिवर्तनीय है। एक बार सेट होने के बाद, आप इसे बदल नहीं सकते। तो आप पहले से जानते हैं कि आपको उस ऑब्जेक्ट के लिए कितनी मेमोरी आवंटित करने की आवश्यकता है।
list
पारस्परिक है। आप इसमें या इससे आइटम जोड़ या हटा सकते हैं। इसे इसका आकार (आंतरिक निहितार्थ के लिए) जानना होगा। यह आवश्यकतानुसार आकार देता है।
कोई मुफ्त भोजन नहीं है - ये क्षमताएं लागत के साथ आती हैं। इसलिए सूचियों के लिए स्मृति में ओवरहेड।
टपल का आकार उपसर्ग है, जिसका अर्थ है ट्यूल इनिशियलाइज़ेशन में इंटरप्रेटर निहित डेटा के लिए पर्याप्त जगह आवंटित करता है, और इसका अंत यह है, यह अपरिवर्तनीय (संशोधित नहीं किया जा सकता है), जबकि एक सूची एक उत्परिवर्ती वस्तु है इसलिए गतिशील गतिशील स्मृति का आबंटन, इसलिए प्रत्येक बार जब आप सूची को जोड़ते या संशोधित करते हैं तो स्थान आवंटित करने से बचें (बदले हुए डेटा को रखने के लिए पर्याप्त स्थान आवंटित करें और उस पर डेटा कॉपी करें), यह भविष्य के परिशिष्ट, संशोधनों, ... के लिए अतिरिक्त स्थान आवंटित करता है। इसे जोड़े।