Python তালিকা তুলনায় আরো দক্ষ tuples হয়?




performance list (6)

এটি তাত্ক্ষণিক এবং উপাদান পুনরুদ্ধার আসে যখন tuples এবং তালিকা মধ্যে কোনো কর্মক্ষমতা পার্থক্য আছে?


সারাংশ

Tuples প্রায় প্রতিটি বিভাগে তালিকা তুলনায় ভাল সঞ্চালন ঝোঁক :

1) Tuples ধ্রুবক folded হতে পারে।

2) Tuples প্রতিলিপি পরিবর্তে পুনরায় ব্যবহার করা যেতে পারে।

3) Tuples কম্প্যাক্ট এবং বেশি বরাদ্দ না।

4) Tuples সরাসরি তাদের উপাদান রেফারেন্স।

Tuples ধ্রুবক folded হতে পারে

ধ্রুবক এর Tuples পাইথন এর peephole অপ্টিমাইজার বা AST-optimizer দ্বারা precomputed করা যেতে পারে। অন্যদিকে তালিকাগুলি স্ক্র্যাচ থেকে তৈরি হয়ে যায়:

    >>> 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 

Tuples কপি করা প্রয়োজন হবে না

রানিং tuple(some_tuple) অবিলম্বে নিজেই ফেরত। যেহেতু tuples অপরিবর্তনীয়, তারা কপি করা হবে না:

>>> 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

Tuples উপর বরাদ্দ না

যেহেতু একটি টুপলের আকার সংশোধন করা হয়েছে, এটি তালিকাগুলির চেয়ে আরও কমপক্ষে সংরক্ষণ করা যেতে পারে যা অতিরিক্ত (append) () ক্রিয়াকলাপগুলি কার্যকর করার জন্য বরাদ্দ করতে হবে।

এই tuples একটি চমৎকার স্থান সুবিধা দেয়:

>>> 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.
 */

Tuples সরাসরি তাদের উপাদান পড়ুন

বস্তুর রেফারেন্স সরাসরি একটি tuple বস্তুর মধ্যে অন্তর্ভুক্ত করা হয়। বিপরীতে, তালিকা পয়েন্টার একটি বহিরাগত অ্যারে indirection একটি অতিরিক্ত স্তর আছে।

এই indexed লুকআপ এবং unpacking জন্য tuples একটি ছোট গতি সুবিধা দেয়:

$ 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

Here কিভাবে tuple (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;

Here [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;

নোট করুন যে টিপলে বস্তুটি সরাসরি দুটি ডাটা পয়েন্টার অন্তর্ভুক্ত করে, যখন তালিকার বস্তুর দুটি ডাটা পয়েন্টার ধারণ করে বহিরাগত অ্যারেতে অনির্দেশনার অতিরিক্ত স্তর থাকে।


Tuples সামান্য আরো দক্ষ এবং কারণ, দ্রুত, তালিকা তুলনায় কারণ তারা অপরিবর্তনীয় হতে হবে।


আপনার তালিকা বা টুপলে সমস্ত আইটেম একই সি টাইপের ক্ষেত্রে যদি আপনি স্ট্যান্ডার্ড লাইব্রেরির 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

আপনি প্রায় অসহনীয় কল করতে পারেন।

তবে নিশ্চিত, তালিকাগুলির তুলনায় 1.239% সময়, বা 1.239% অতিরিক্ত সময় নেয়।


সাধারণত, আপনি tuples সামান্য দ্রুত হতে পারে আশা করতে পারেন। তবে অবশ্যই আপনার নির্দিষ্ট ক্ষেত্রে পরীক্ষা করা উচিত (যদি পার্থক্য আপনার প্রোগ্রামের কার্যকারিতাকে প্রভাবিত করতে পারে - মনে রাখবেন "অকালিক অপটিমাইজেশনটি সমস্ত মন্দিরের মূল")।

Python এই খুব সহজ করে তোলে: 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

তাই এই ক্ষেত্রে, তাত্ক্ষণিকতাটি টুপলের জন্য তত্ক্ষণাত্তরের দ্রুততম ক্রম, তবে বস্তুর অ্যাক্সেস আসলে তালিকাটির জন্য কিছুটা দ্রুত! তাই যদি আপনি কয়েকটি টিপল তৈরি করেন এবং অনেকগুলি অ্যাক্সেস করেন তবে এটি পরিবর্তে তালিকাগুলি ব্যবহার করতে দ্রুত হতে পারে।

অবশ্যই যদি আপনি কোন আইটেমটি পরিবর্তন করতে চান তবে তালিকাটি অবশ্যই দ্রুততর হবে কারণ এটির একটি আইটেম পরিবর্তন করার জন্য আপনাকে একটি সম্পূর্ণ নতুন টিপল তৈরি করতে হবে (যেহেতু টুপগুলি অপরিবর্তনীয় হয়)।


dis মডিউলটি একটি ফাংশনের জন্য বাইট কোডটিকে পৃথক করে এবং টুপল এবং তালিকাগুলির মধ্যে পার্থক্য দেখতে দরকারী।

এই ক্ষেত্রে, আপনি দেখতে পারেন যে একটি উপাদান অ্যাক্সেস করা একই কোড জেনারেট করে, কিন্তু একটি টুপি বরাদ্দ করা একটি তালিকা নির্ধারণের চেয়ে অনেক দ্রুত।

>>> 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




python-internals