টিপলগুলি তালিকার তুলনায় মেমরিতে কম স্থান নেয় কেন?


105

একটি tupleপাইথন কম মেমরি স্পেস লাগে:

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

যেখানে listগুলি আরও মেমরির স্থান নেয়:

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

পাইথন মেমরি পরিচালনায় অভ্যন্তরীণভাবে কী ঘটে?


1
এটি অভ্যন্তরীণভাবে কীভাবে কাজ করে সে সম্পর্কে আমি নিশ্চিত নই, তবে তালিকার অবজেক্টটিতে কমপক্ষে আরও কার্যকারিতা রয়েছে যেমন টুপলটি নেই app অতএব এটি
টিপলটির

আমি মনে করি এটি মেশিন থেকে মেশিনের উপরও নির্ভর করে .... আমার জন্য যখন আমি একটি = (1,2,3) পরীক্ষা করি তখন 72 এবং খ = [1,2,3] লাগে 88
অমৃত

6
পাইথন টিপলস অপরিবর্তনীয়। মিউটেবল অবজেক্টগুলির রানটাইমের পরিবর্তনের সাথে সম্পর্কিত অতিরিক্ত ওভারহেড থাকে head
লি ড্যানিয়েল ক্রকার

@ মিটারেভেন কোনও ধরণের পদ্ধতিগুলির উদাহরণগুলি গ্রহণ করা মেমরির জায়গাকে প্রভাবিত করে না। পদ্ধতি তালিকা এবং তাদের কোড অবজেক্ট প্রোটোটাইপ দ্বারা পরিচালিত হয়, তবে উদাহরণগুলি কেবল ডেটা এবং অভ্যন্তরীণ ভেরিয়েবল সঞ্চয় করে।
jjmontes

উত্তর:


144

আমি ধরে নিচ্ছি আপনি সিপিথন ব্যবহার করছেন এবং bits৪ বিট (আমি আমার সিপিথন ২.7৪-বিট-তে একই ফলাফল পেয়েছি)। অন্যান্য পাইথন বাস্তবায়নে বা আপনার 32 বাইট পাইথন থাকলে পার্থক্য থাকতে পারে।

বাস্তবায়ন নির্বিশেষে, listগুলি ভেরিয়েবল-আকারের এবং tupleগুলি স্থির আকারের হয়।

সুতরাং tupleগুলি স্ট্রাক্টের মধ্যে সরাসরি উপাদানগুলি সঞ্চয় করতে পারে, অন্যদিকে তালিকাগুলির জন্য ইন্ডিরিয়ারেশনের একটি স্তর প্রয়োজন (এটি উপাদানগুলির একটি পয়েন্টার সঞ্চয় করে)। ইন্ডিয়ারেশনের এই স্তরটি একটি পয়েন্টার, 64৪ বিট সিস্টেমে যেটি bit৪ বিট, তাই 8 বাইটস।

তবে আর একটি জিনিস যা listকরতে পারে: তারা অতিরিক্ত বরাদ্দ দেয়। অন্যথায় list.appendএটি সর্বদা একটি O(n)অপারেশন হতে পারে - এটিকে আরও বেশি পরিমাণে বরাদ্দ দেওয়ার (আরও দ্রুত !!!) তৈরি করার জন্য । তবে এখন এটি বরাদ্দ করা আকার এবং ভরাট আকারের ( কেবলমাত্র একটি আকার সংরক্ষণের প্রয়োজন, কারণ বরাদ্দ এবং ভরাট আকার সর্বদা অভিন্ন থাকে) তার নজর রাখতে হবে । তার মানে প্রতিটি তালিকায় আরেকটি "আকার" সংরক্ষণ করতে হবে যা 64৪ বিট সিস্টেমে একটি bit৪ বিট পূর্ণসংখ্যা, আবার ৮ বাইট।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বস্তুগুলি পাইথন অবজেক্ট এবং সিপথন এমনকি ছোট ছোট পূর্ণসংখ্যার পুনরায় ব্যবহার করে, তাই স্মৃতিতে থাকা অবজেক্টগুলির সম্ভবত আরও সঠিক উপস্থাপনা (যদিও পাঠযোগ্য নয়) হ'ল:

এখানে চিত্র বর্ণনা লিখুন

উপকারী সংজুক:

নোট করুন যে __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__ )।


পুনরায় " সুতরাং তালিকাগুলির তুলনায় টিপলসের চেয়ে কমপক্ষে 16 বাইটের বেশি স্মৃতি দরকার " ", এটি 8 হবে না? টিপলসের জন্য একটি আকার এবং তালিকার জন্য দুটি আকারের অর্থ তালিকাগুলির জন্য এক অতিরিক্ত আকার।
ইকেগামী

1
হ্যাঁ, তালিকায় একটি অতিরিক্ত "আকার" (8 বাইট) রয়েছে তবে এটি স্ট্রাক্টে সরাসরি স্ট্রুপের পরিবর্তে "পাইপবজেক্টের অ্যারে" তে একটি পয়েন্টার (8 বাইট) সঞ্চয় করে (টিউপল কী করে)। 8 + 8 থেকে = 16।
এমসিফার্ট

2
আরেকটি দরকারী সম্পর্কে লিংক listমেমরি অ্যালোকেশন stackoverflow.com/questions/40018398/...
vishes_shell

@vishes_shell এটি প্রশ্নের সাথে সত্যই সম্পর্কিত নয় কারণ প্রশ্নের কোডটি মোটেই বেশি বরাদ্দ দেয় না । তবে হ্যাঁ এটি কার্যকর যদি আপনি ব্যবহারের সময় অতিরিক্ত বরাদ্দের পরিমাণ list()বা একটি তালিকা বোঝার ক্ষেত্রে আরও জানতে চান useful
এমসিফার্ট

1
@ user3349993 টিপলগুলি অপরিবর্তনীয়, তাই আপনি একটি টিপল এ সংযুক্ত করতে পারবেন না বা টিপল থেকে কোনও আইটেম সরাতে পারবেন না।
এমসিফার্ট

31

আমি সিপিথন কোডবেসে আরও গভীর ডুব নেব যাতে আকারগুলি কীভাবে গণনা করা হয় তা আমরা দেখতে পারি। আপনার নির্দিষ্ট উদাহরণে , কোনও অতিরিক্ত বরাদ্দ কার্যকর করা হয়নি, সুতরাং আমি এটির উপরে স্পর্শ করব না

আমি এখানে যেমন 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)একটি ম্যাক্রো যে grabs হয় ob_typeএর self(ফিরে PyList_Type) যখন _PyObject_SIZEঅন্য ম্যাক্রো যে দখল tp_basicsizeকরে ধরণ থেকে। tp_basicsizeহিসাবে গণনা করা হয় sizeof(PyListObject)যেখানে PyListObjectউদাহরণস্বরূপ struct হয় হয়।

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এটি আমি সন্ধান করতে সক্ষম হইনি been (সম্ভাব্য ব্যাখ্যার জন্য এমসিফার্টের মন্তব্য দেখুন)


তবে, এটি আপনার নির্দিষ্ট উদাহরণের মধ্যে পার্থক্য । listগুলি অনেকগুলি বরাদ্দ উপাদানগুলির আশেপাশে রাখে যা কখন অতিরিক্ত বরাদ্দ দেওয়া হয় তা নির্ধারণে সহায়তা করে।

এখন, যখন অতিরিক্ত উপাদান যুক্ত করা হয়, তালিকাগুলি O (1) সংযোজনগুলি অর্জনের জন্য সত্যই এই অতিরিক্ত বরাদ্দটি সম্পাদন করে। এমপিফার্টস এর উত্তরে সুন্দরভাবে কভার করায় এর ফলে এটি আরও বৃহত্তর আকারের হতে পারে।


আমি বিশ্বাস করি এটি ob_item[1]বেশিরভাগই স্থানধারক (সুতরাং এটি বুনিয়াদি থেকে বিয়োগ করে বোঝায়)। tupleব্যবহার বরাদ্দ করা হয় PyObject_NewVar। আমি বিশদটি সন্ধান করতে পারিনি যাতে এটি কেবল একটি শিক্ষিত অনুমান ...
ম্যাসিফের্ট

@ এসিফার্ট এর জন্য দুঃখিত, স্থির :-)। আমি সত্যিই জানি না, আমি অতীতে এটি খুঁজে পাওয়ার কথা মনে করেছি কিন্তু আমি কখনই এটিকে খুব বেশি মনোযোগ দিচ্ছি না, সম্ভবত আমি ভবিষ্যতের কোনও সময়ে একটি প্রশ্ন জিজ্ঞাসা করব :-)
দিমিত্রিস ফ্যাসারাকিস হিলিয়ার্ড

29

এমসিফার্ট উত্তরটি এটিকে বিস্তৃতভাবে আবরণ করে; এটিকে সহজ রাখতে আপনি ভাবতে পারেন:

tupleঅপরিবর্তনীয় এটি সেট হয়ে গেলে আপনি এটি পরিবর্তন করতে পারবেন না। সুতরাং আপনি আগে থেকেই জানেন যে আপনাকে সেই বস্তুর জন্য কত মেমরি বরাদ্দ করতে হবে।

listপরিবর্তনীয়। আপনি এটিতে বা এটি থেকে আইটেমগুলি যুক্ত করতে বা সরাতে পারেন। এটি এর আকারটি জানতে হবে (অভ্যন্তরীণ ইমপ্লিমেন্টের জন্য)। এটি প্রয়োজন অনুসারে আকার পরিবর্তন করে।

কোনও নিখরচায় খাবার নেই - এই ক্ষমতাগুলি ব্যয় সহ আসে। অতএব তালিকার জন্য মেমরির ওভারহেড।


3

টিউলের আকারটি উপসর্গযুক্ত, অর্থাত্ দ্বিপ্রান্তের সূচনাতে দোভাষী অন্তর্ভুক্ত ডেটার জন্য পর্যাপ্ত জায়গা বরাদ্দ করেন এবং এটি শেষ হয়ে যায়, এটিকে অপরিবর্তনীয় (সংশোধন করা যায় না) দেওয়া হয়, তবে একটি তালিকা একটি পরিবর্তনীয় অবজেক্ট তাই গতিশীল বোঝায় মেমরির বরাদ্দ, সুতরাং প্রতিবারের জন্য তালিকা সংযোজন বা সংশোধন করার জন্য স্থান বরাদ্দ এড়াতে (পরিবর্তিত ডেটা ধারণ করার জন্য পর্যাপ্ত জায়গা বরাদ্দ করুন এবং এতে ডেটা অনুলিপি করুন), এটি ভবিষ্যতের সংযোজন, পরিবর্তন, ... জন্য অতিরিক্ত জায়গা বরাদ্দ করে দেয় যা অনেক বেশি এটির পরিমাণ

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.