क्या तांत्रिकों और सूचियों के बीच कोई प्रदर्शन अंतर है जब यह तत्वों की तात्कालिकता और पुनर्प्राप्ति की बात आती है?
क्या तांत्रिकों और सूचियों के बीच कोई प्रदर्शन अंतर है जब यह तत्वों की तात्कालिकता और पुनर्प्राप्ति की बात आती है?
जवाबों:
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
ListLike
एक साथ __getitem__
कि कुछ बुरी तरह धीमी गति से, तो जुदा करता है x = ListLike((1, 2, 3, 4, 5)); y = x[2]
। सूची उदाहरण की तुलना में बाईटकोड ऊपर की ओर टपल उदाहरण की तरह होगा, लेकिन क्या आप वास्तव में मानते हैं कि प्रदर्शन समान होगा?
सामान्य तौर पर, आप उम्मीद कर सकते हैं कि ट्यूपल्स थोड़े तेज़ होंगे। हालाँकि, आपको निश्चित रूप से अपने विशिष्ट मामले का परीक्षण करना चाहिए (यदि अंतर आपके कार्यक्रम के प्रदर्शन को प्रभावित कर सकता है - याद रखें "समय से पहले अनुकूलन सभी बुराई की जड़ है)।
अजगर बहुत आसान इस बनाता है: 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
तो इस मामले में, ताबूत के लिए तात्कालिकता लगभग तीव्रता का एक क्रम है, लेकिन आइटम का उपयोग वास्तव में सूची के लिए कुछ तेज है! इसलिए यदि आप कुछ ट्यूपल्स बना रहे हैं और उन्हें कई बार एक्सेस कर रहे हैं, तो वास्तव में इसके बजाय सूचियों का उपयोग करना तेज हो सकता है।
बेशक, यदि आप किसी आइटम को बदलना चाहते हैं , तो सूची निश्चित रूप से तेज़ होगी क्योंकि आपको इसमें से एक आइटम को बदलने के लिए पूरी तरह से नया टपल बनाने की आवश्यकता होगी (चूंकि ट्यूप्स अपरिवर्तनीय हैं)।
python -m timeit "x=tuple(xrange(999999))"
बनाम python -m timeit "x=list(xrange(999999))"
। जैसा कि कोई उम्मीद कर सकता है, एक सूची की तुलना में एक टपल को थोड़ा अधिक समय लगता है।
-s "SETUP_CODE"
वास्तविक समय कोड से पहले चलाया जाता है।
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;
ध्यान दें कि टपल ऑब्जेक्ट दो डेटा पॉइंटर्स को सीधे शामिल करता है जबकि सूची ऑब्जेक्ट में दो डेटा पॉइंटर्स को पकड़े हुए एक बाहरी सरणी के लिए अप्रत्यक्ष की एक अतिरिक्त परत होती है।
Internally, tuples are stored a little more efficiently than lists, and also tuples can be accessed slightly faster.
आप डीएफ के जवाब से परिणाम कैसे समझा सकते हैं?
tuple(some_tuple)
केवल तब ही वापस आता some_tuple
है some_tuple
जब धो सकते हैं - जब इसकी सामग्री पुनरावर्ती अपरिवर्तनीय और धोने योग्य हो। अन्यथा, tuple(some_tuple)
नया टपल लौटाता है। उदाहरण के लिए, जब some_tuple
परस्पर आइटम शामिल होते हैं।
टुपल्स, अपरिवर्तनीय होने के कारण, अधिक स्मृति कुशल हैं; कार्यकुशलता के लिए सूचियाँ, समग्र मेमोरी को क्रम में निरंतर realloc
एस के बिना अनुमति देने के लिए । इसलिए, यदि आप अपने कोड (जैसे for direction in 'up', 'right', 'down', 'left':
) में मूल्यों के निरंतर अनुक्रम के माध्यम से पुनरावृति करना चाहते हैं, तो ट्यूपल्स को प्राथमिकता दी जाती है, क्योंकि इस तरह के ट्यूपल्स को संकलन समय में पूर्व-गणना की जाती है।
एक्सेस गति समान होनी चाहिए (वे दोनों मेमोरी में सन्निहित सरणियों के रूप में संग्रहीत हैं)।
लेकिन, जब आप उत्परिवर्तित डेटा से निपटते हैं , तो alist.append(item)
इसे बहुत पसंद किया जाता है atuple+= (item,)
। याद रखें, टुपल्स को फील्ड नामों के बिना रिकॉर्ड के रूप में माना जाता है।
आपको array
मानक लाइब्रेरी में मॉड्यूल पर भी विचार करना चाहिए, यदि आपकी सूची या टपल में सभी आइटम एक ही सी प्रकार के हैं। यह कम मेमोरी लेगा और तेज हो सकता है।
यहाँ एक और छोटा बेंचमार्क है, बस इसके लिए ..
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%
अतिरिक्त समय लिया ।
ट्यूपल थोड़ा अधिक कुशल होना चाहिए और इसके कारण, सूचियों की तुलना में तेज़, क्योंकि वे अपरिवर्तनीय हैं।
टपल पढ़ने में बहुत कुशल होने का मुख्य कारण है क्योंकि यह अपरिवर्तनीय है।
इसका कारण है टुपल्स को सूचियों के विपरीत मेमोरी कैश में संग्रहित किया जा सकता है। प्रोग्राम हमेशा सूचियों के मेमोरी स्थान से पढ़ता है क्योंकि यह परिवर्तनशील है (किसी भी समय बदल सकता है)।