क्या पायथन में सूचियों की तुलना में ट्यूपल अधिक कुशल हैं?


225

क्या तांत्रिकों और सूचियों के बीच कोई प्रदर्शन अंतर है जब यह तत्वों की तात्कालिकता और पुनर्प्राप्ति की बात आती है?



2
यदि आप के बीच उपयोग की जाने वाली स्मृति में रुचि रखते हैं, तो इस प्रकार के प्लॉट देखें जो मैंने बनाया है: stackoverflow.com/a/30008338/2087463
tmthydvnprt

जवाबों:


172

disमॉड्यूल एक समारोह के लिए बाइट कोड disassembles और tuples और सूचियों के बीच अंतर को देखने के लिए उपयोगी है।

इस स्थिति में, आप देख सकते हैं कि एक तत्व तक पहुँच समान कोड उत्पन्न करता है, लेकिन यह है कि एक सूची सौंपने की तुलना में एक ट्यूपल असाइन करना बहुत तेज़ है।

>>> def a():
...     x=[1,2,3,4,5]
...     y=x[2]
...
>>> def b():
...     x=(1,2,3,4,5)
...     y=x[2]
...
>>> import dis
>>> dis.dis(a)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 LOAD_CONST               3 (3)
              9 LOAD_CONST               4 (4)
             12 LOAD_CONST               5 (5)
             15 BUILD_LIST               5
             18 STORE_FAST               0 (x)

  3          21 LOAD_FAST                0 (x)
             24 LOAD_CONST               2 (2)
             27 BINARY_SUBSCR
             28 STORE_FAST               1 (y)
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE
>>> dis.dis(b)
  2           0 LOAD_CONST               6 ((1, 2, 3, 4, 5))
              3 STORE_FAST               0 (x)

  3           6 LOAD_FAST                0 (x)
              9 LOAD_CONST               2 (2)
             12 BINARY_SUBSCR
             13 STORE_FAST               1 (y)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

66
इर, बस एक ही बायोटेक उत्पन्न होता है बिल्कुल इसका मतलब यह नहीं है कि सी (और इसलिए सीपीयू) स्तर पर समान संचालन होते हैं। एक वर्ग बनाने का प्रयास करें ListLikeएक साथ __getitem__कि कुछ बुरी तरह धीमी गति से, तो जुदा करता है x = ListLike((1, 2, 3, 4, 5)); y = x[2]। सूची उदाहरण की तुलना में बाईटकोड ऊपर की ओर टपल उदाहरण की तरह होगा, लेकिन क्या आप वास्तव में मानते हैं कि प्रदर्शन समान होगा?
मेज़

2
ऐसा लगता है कि आप कह रहे हैं कि कुछ प्रकार दूसरों की तुलना में अधिक कुशल हैं। यह समझ में आता है, लेकिन सूची और टुपल पीढ़ियों का ओवरहेड शामिल डेटा प्रकार के लिए ऑर्थोगोनल प्रतीत होता है, कैवेट के साथ कि वे एक ही डेटा प्रकार की सूची और टुपल्स हैं।
मार्क हैरिसन

11
बाइट-कोडों की संख्या, जैसे कि लाइनों की संख्या, गति-से-निष्पादन (और इसलिए दक्षता और प्रदर्शन) के लिए बहुत कम संबंध रखती है।
मार्टीन्यू

18
हालाँकि, आप जो भी सुझाव दे सकते हैं, वह ऑप्स की गणना से कुछ भी गलत है, यह महत्वपूर्ण अंतर दिखाता है: निरंतर ट्यूपल्स को इस तरह के बाइटकोड में संग्रहीत किया जाता है और जब उपयोग किया जाता है तो केवल संदर्भित किया जाता है, जबकि सूचियों को रनटाइम पर बनाने की आवश्यकता होती है।
poolie

6
यह उत्तर हमें दिखाता है कि पायथन टपल स्थिरांक को स्वीकार करता है। यह जानकर अच्छा लगा! लेकिन क्या होता है जब एक टपल या वैरिएबल वैल्यू से लिस्ट बनाने की कोशिश की जाती है?
टॉम

211

सामान्य तौर पर, आप उम्मीद कर सकते हैं कि ट्यूपल्स थोड़े तेज़ होंगे। हालाँकि, आपको निश्चित रूप से अपने विशिष्ट मामले का परीक्षण करना चाहिए (यदि अंतर आपके कार्यक्रम के प्रदर्शन को प्रभावित कर सकता है - याद रखें "समय से पहले अनुकूलन सभी बुराई की जड़ है)।

अजगर बहुत आसान इस बनाता है: timeit अपने दोस्त है।

$ python -m timeit "x=(1,2,3,4,5,6,7,8)"
10000000 loops, best of 3: 0.0388 usec per loop

$ python -m timeit "x=[1,2,3,4,5,6,7,8]"
1000000 loops, best of 3: 0.363 usec per loop

तथा...

$ python -m timeit -s "x=(1,2,3,4,5,6,7,8)" "y=x[3]"
10000000 loops, best of 3: 0.0938 usec per loop

$ python -m timeit -s "x=[1,2,3,4,5,6,7,8]" "y=x[3]"
10000000 loops, best of 3: 0.0649 usec per loop

तो इस मामले में, ताबूत के लिए तात्कालिकता लगभग तीव्रता का एक क्रम है, लेकिन आइटम का उपयोग वास्तव में सूची के लिए कुछ तेज है! इसलिए यदि आप कुछ ट्यूपल्स बना रहे हैं और उन्हें कई बार एक्सेस कर रहे हैं, तो वास्तव में इसके बजाय सूचियों का उपयोग करना तेज हो सकता है।

बेशक, यदि आप किसी आइटम को बदलना चाहते हैं , तो सूची निश्चित रूप से तेज़ होगी क्योंकि आपको इसमें से एक आइटम को बदलने के लिए पूरी तरह से नया टपल बनाने की आवश्यकता होगी (चूंकि ट्यूप्स अपरिवर्तनीय हैं)।


3
अजगर के किस संस्करण के साथ आपके परीक्षण थे!
मैट जॉइनर

2
एक और दिलचस्प परीक्षण है - python -m timeit "x=tuple(xrange(999999))"बनाम python -m timeit "x=list(xrange(999999))"। जैसा कि कोई उम्मीद कर सकता है, एक सूची की तुलना में एक टपल को थोड़ा अधिक समय लगता है।
हमीश ग्रुबीजन

3
ऐसा लगता है कि सूची उपयोग की तुलना में टुपल का उपयोग धीमा है। हालांकि, मेरे विंडोज 7 पीसी पर पायथन 2.7 में यह कोशिश करते हुए कि अंतर केवल 10% है, इसलिए महत्वहीन है।
टूलमेकरसेव

51
एफडब्ल्यूआईडब्ल्यू, सूची का उपयोग तेज है जो पायथन 2 में पहुंचता है, लेकिन केवल इसलिए कि पायथन / केवलासी में BINARY_SUBSCR में सूचियों के लिए एक विशेष मामला है। पायथन 3 में, वह ऑप्टिमाइज़ेशन चला गया है, और टुपल्स एक्सेस लिस्ट एक्सेस की तुलना में थोड़ा तेज़ हो गया है।
रेमंड हेटिंगर

3
@ योओपू, पहला परीक्षण एक लाख बार सूची बनाता है, लेकिन दूसरा एक बार एक सूची बनाता है और एक लाख बार इसे एक्सेस करता है। -s "SETUP_CODE"वास्तविक समय कोड से पहले चलाया जाता है।
लेवेज

203

सारांश

Tuples सूचियों की तुलना में बेहतर प्रदर्शन करते हैं लगभग हर श्रेणी में :

1) ट्यूपल को लगातार मोड़ा जा सकता है

2) नकल के बजाय टुपल्स का पुन: उपयोग किया जा सकता है।

3) टुपल्स कॉम्पैक्ट हैं और ओवर-आवंटित नहीं हैं।

4) ट्यूपल्स सीधे अपने तत्वों को संदर्भित करते हैं।

ट्यूपल्स को लगातार मोड़ा जा सकता है

स्थिरांक के टप्ल्स को पायथन के पीपहोल ऑप्टिमाइज़र या एएसटी-ऑप्टिमाइज़र द्वारा पूर्वनिर्मित किया जा सकता है। दूसरी ओर, सूची, खरोंच से निर्मित हो जाती है:

    >>> from dis import dis

    >>> dis(compile("(10, 'abc')", '', 'eval'))
      1           0 LOAD_CONST               2 ((10, 'abc'))
                  3 RETURN_VALUE   

    >>> dis(compile("[10, 'abc']", '', 'eval'))
      1           0 LOAD_CONST               0 (10)
                  3 LOAD_CONST               1 ('abc')
                  6 BUILD_LIST               2
                  9 RETURN_VALUE 

टुपल्स को कॉपी करने की आवश्यकता नहीं है

रनिंग tuple(some_tuple)रिटर्न तुरंत ही। चूंकि ट्यूप्स अपरिवर्तनीय हैं, उन्हें कॉपी करने की आवश्यकता नहीं है:

>>> a = (10, 20, 30)
>>> b = tuple(a)
>>> a is b
True

इसके विपरीत, list(some_list)सभी डेटा को एक नई सूची में कॉपी करने की आवश्यकता होती है:

>>> a = [10, 20, 30]
>>> b = list(a)
>>> a is b
False

टुपल्स अधिक आवंटित नहीं करते हैं

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

इससे टुपल्स को एक अच्छा स्थान लाभ मिलता है:

>>> import sys
>>> sys.getsizeof(tuple(iter(range(10))))
128
>>> sys.getsizeof(list(iter(range(10))))
200

यहाँ ऑब्जेक्ट्स / listobject.c की टिप्पणी है जो बताती है कि सूचियाँ क्या कर रही हैं:

/* This over-allocates proportional to the list size, making room
 * for additional growth.  The over-allocation is mild, but is
 * enough to give linear-time amortized behavior over a long
 * sequence of appends() in the presence of a poorly-performing
 * system realloc().
 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
 * Note: new_allocated won't overflow because the largest possible value
 *       is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
 */

ट्यूपल्स सीधे अपने तत्वों को संदर्भित करते हैं

वस्तुओं के संदर्भ को सीधे एक टूप्ले ऑब्जेक्ट में शामिल किया जाता है। इसके विपरीत, सूचियों में बाहरी सरणी के संकेत के लिए अप्रत्यक्ष रूप से एक अतिरिक्त परत होती है।

यह ट्यूपल्स को अनुक्रमित लुक्स और अनपैकिंग के लिए एक छोटी गति का लाभ देता है:

$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'a[1]'
10000000 loops, best of 3: 0.0304 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'a[1]'
10000000 loops, best of 3: 0.0309 usec per loop

$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'x, y, z = a'
10000000 loops, best of 3: 0.0249 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'x, y, z = a'
10000000 loops, best of 3: 0.0251 usec per loop

यहाँ बताया गया है कि कैसे टपल (10, 20)को संग्रहीत किया जाता है:

    typedef struct {
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
        Py_ssize_t ob_size;
        PyObject *ob_item[2];     /* store a pointer to 10 and a pointer to 20 */
    } PyTupleObject;

यहाँ बताया गया है कि सूची कैसे [10, 20]संग्रहीत है:

    PyObject arr[2];              /* store a pointer to 10 and a pointer to 20 */

    typedef struct {
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
        Py_ssize_t ob_size;
        PyObject **ob_item = arr; /* store a pointer to the two-pointer array */
        Py_ssize_t allocated;
    } PyListObject;

ध्यान दें कि टपल ऑब्जेक्ट दो डेटा पॉइंटर्स को सीधे शामिल करता है जबकि सूची ऑब्जेक्ट में दो डेटा पॉइंटर्स को पकड़े हुए एक बाहरी सरणी के लिए अप्रत्यक्ष की एक अतिरिक्त परत होती है।


19
अंत में, कोई व्यक्ति सी संरचना रखता है!
ओसमैन

1
Internally, tuples are stored a little more efficiently than lists, and also tuples can be accessed slightly faster. आप डीएफ के जवाब से परिणाम कैसे समझा सकते हैं?
14

5
जब ~ 100k सूचियों की ~ 50k सूचियों के साथ काम करते हैं, तो इस संरचना को ट्यूपल्स पर ले जाने से कई प्रतिरूपों के लिए परिमाण के कई आदेशों से लुकअप समय कम हो जाता है। मेरा मानना ​​है कि एक बार जब आप अपने द्वारा प्रदर्शित की गई अप्रत्यक्षता की दूसरी परत को हटाने के कारण टपल का उपयोग करना शुरू कर देते हैं, तो टुपल के अधिक कैश लोकेलिटी के कारण ऐसा होता है।
horta

tuple(some_tuple)केवल तब ही वापस आता some_tupleहै some_tupleजब धो सकते हैं - जब इसकी सामग्री पुनरावर्ती अपरिवर्तनीय और धोने योग्य हो। अन्यथा, tuple(some_tuple)नया टपल लौटाता है। उदाहरण के लिए, जब some_tupleपरस्पर आइटम शामिल होते हैं।
लुसियानो रामलहो

ट्यूपल हमेशा तेज नहीं होते हैं। रेंज में `कॉन्सडर` `टी = () के लिए (1,100): टी + = आईएल = [] के लिए मैं रेंज में (1,1000): a.append (i)` `` दूसरा वाला तेज है
मेल्विल जेम्स

32

टुपल्स, अपरिवर्तनीय होने के कारण, अधिक स्मृति कुशल हैं; कार्यकुशलता के लिए सूचियाँ, समग्र मेमोरी को क्रम में निरंतर reallocएस के बिना अनुमति देने के लिए । इसलिए, यदि आप अपने कोड (जैसे for direction in 'up', 'right', 'down', 'left':) में मूल्यों के निरंतर अनुक्रम के माध्यम से पुनरावृति करना चाहते हैं, तो ट्यूपल्स को प्राथमिकता दी जाती है, क्योंकि इस तरह के ट्यूपल्स को संकलन समय में पूर्व-गणना की जाती है।

एक्सेस गति समान होनी चाहिए (वे दोनों मेमोरी में सन्निहित सरणियों के रूप में संग्रहीत हैं)।

लेकिन, जब आप उत्परिवर्तित डेटा से निपटते हैं , तो alist.append(item)इसे बहुत पसंद किया जाता है atuple+= (item,)। याद रखें, टुपल्स को फील्ड नामों के बिना रिकॉर्ड के रूप में माना जाता है।


1
अजगर में संकलन समय क्या है?
बाल्की

1
@balki: वह समय जब अजगर स्रोत को बायोटकोड (जिसे बायटेकोड को .py [सह] फ़ाइल के रूप में सहेजा जा सकता है) से संकलित किया जाता है।
tzot 7

यदि संभव हो तो एक प्रशस्ति पत्र महान होगा।
बृजेश चौहान

9

आपको arrayमानक लाइब्रेरी में मॉड्यूल पर भी विचार करना चाहिए, यदि आपकी सूची या टपल में सभी आइटम एक ही सी प्रकार के हैं। यह कम मेमोरी लेगा और तेज हो सकता है।


15
यह कम मेमोरी लेगा, लेकिन एक्सेस टाइम तेजी से कम होने के बजाय थोड़ा धीमा हो जाएगा। एक तत्व तक पहुंचने के लिए पैक किए गए मान को वास्तविक पूर्णांक के लिए अनबॉक्स करने की आवश्यकता होती है, जो प्रक्रिया को धीमा कर देगा।
ब्रायन

5

यहाँ एक और छोटा बेंचमार्क है, बस इसके लिए ..

In [11]: %timeit list(range(100))
749 ns ± 2.41 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [12]: %timeit tuple(range(100))
781 ns ± 3.34 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [1]: %timeit list(range(1_000))
13.5 µs ± 466 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [2]: %timeit tuple(range(1_000))
12.4 µs ± 182 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: %timeit list(range(10_000))
182 µs ± 810 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [8]: %timeit tuple(range(10_000))
188 µs ± 2.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [3]: %timeit list(range(1_00_000))
2.76 ms ± 30.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [4]: %timeit tuple(range(1_00_000))
2.74 ms ± 31.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [10]: %timeit list(range(10_00_000))
28.1 ms ± 266 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [9]: %timeit tuple(range(10_00_000))
28.5 ms ± 447 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

आइए इनको औसत करें:

In [3]: l = np.array([749 * 10 ** -9, 13.5 * 10 ** -6, 182 * 10 ** -6, 2.76 * 10 ** -3, 28.1 * 10 ** -3])

In [2]: t = np.array([781 * 10 ** -9, 12.4 * 10 ** -6, 188 * 10 ** -6, 2.74 * 10 ** -3, 28.5 * 10 ** -3])

In [11]: np.average(l)
Out[11]: 0.0062112498000000006

In [12]: np.average(t)
Out[12]: 0.0062882362

In [17]: np.average(t) / np.average(l)  * 100
Out[17]: 101.23946713590554

आप इसे लगभग अनिर्णायक कह सकते हैं।

लेकिन निश्चित रूप से, ट्यूपल्स ने सूचियों की तुलना में काम करने के लिए 101.239%समय, या 1.239%अतिरिक्त समय लिया ।


4

ट्यूपल थोड़ा अधिक कुशल होना चाहिए और इसके कारण, सूचियों की तुलना में तेज़, क्योंकि वे अपरिवर्तनीय हैं।


4
आप यह क्यों कहते हैं कि अपरिवर्तनीयता, अपने आप में, दक्षता बढ़ाती है? विशेष रूप से तात्कालिकता और पुनर्प्राप्ति की दक्षता?
ब्लेयर कोनराड

1
ऐसा लगता है कि मेरे ऊपर मार्क के जवाब ने पायथन के अंदर क्या होता है, के डिसाइड किए गए निर्देशों को कवर किया है। आप देख सकते हैं कि तात्कालिकता कम निर्देश लेती है, हालांकि इस मामले में, पुनर्प्राप्ति स्पष्ट रूप से समान है।
ctcherry

अपरिवर्तनीय tuples में उत्परिवर्ती सूचियों की तुलना में अधिक त्वरित पहुँच है
noobninja

-6

टपल पढ़ने में बहुत कुशल होने का मुख्य कारण है क्योंकि यह अपरिवर्तनीय है।

अपरिवर्तनीय वस्तुओं को पढ़ना आसान क्यों है?

इसका कारण है टुपल्स को सूचियों के विपरीत मेमोरी कैश में संग्रहित किया जा सकता है। प्रोग्राम हमेशा सूचियों के मेमोरी स्थान से पढ़ता है क्योंकि यह परिवर्तनशील है (किसी भी समय बदल सकता है)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.