ছোট তালিকার চেয়ে ছোট স্ট্রিংয়ের মাধ্যমে পুনরাবৃত্তি করা কেন ধীর?


132

আমি সময়োপযোগী হয়ে খেলছিলাম এবং লক্ষ্য করেছি যে একটি ছোট স্ট্রিংয়ের উপর একটি সাধারণ তালিকা বোঝার জন্য ছোট একক অক্ষরের স্ট্রিংয়ের তালিকায় একই ক্রিয়াকলাপটি করা বেশি সময় নেয়। কোন ব্যাখ্যা? এটি প্রায় 1.35 গুণ বেশি সময়।

>>> from timeit import timeit
>>> timeit("[x for x in 'abc']")
2.0691067844831528
>>> timeit("[x for x in ['a', 'b', 'c']]")
1.5286479570345861

নিম্ন স্তরে কী হচ্ছে যা এর ফলে ঘটছে?

উত্তর:


193

টি এল; ডিআর

  • পাইথন 2 এর জন্য প্রচুর ওভারহেড সরিয়ে ফেলার পরে প্রকৃত গতির পার্থক্য 70% (বা আরও বেশি) এর কাছাকাছি।

  • অবজেক্ট সৃষ্টি না ভুল। এক-বর্ণের স্ট্রিং ক্যাশে হওয়ায় কোনও পদ্ধতিই একটি নতুন অবজেক্ট তৈরি করে না।

  • পার্থক্যটি অকাট্য, তবে স্ট্রিং ইনডেক্সিংয়ের ক্ষেত্রে বৃহত্তর সংখ্যক চেকগুলি থেকে তৈরি করা হয়েছে, প্রকার এবং সুগঠনের ক্ষেত্রে। এটি কী প্রত্যাবর্তন করবে তা যাচাই করার প্রয়োজনের জন্য এটি বেশ সম্ভবত ধন্যবাদ thanks

  • তালিকা সূচক উল্লেখযোগ্যভাবে দ্রুত।



>>> python3 -m timeit '[x for x in "abc"]'
1000000 loops, best of 3: 0.388 usec per loop

>>> python3 -m timeit '[x for x in ["a", "b", "c"]]'
1000000 loops, best of 3: 0.436 usec per loop

আপনি যা খুঁজে পেয়েছেন তাতে এটি একমত নয় ...

আপনার অবশ্যই পাইথন 2 ব্যবহার করা উচিত।

>>> python2 -m timeit '[x for x in "abc"]'
1000000 loops, best of 3: 0.309 usec per loop

>>> python2 -m timeit '[x for x in ["a", "b", "c"]]'
1000000 loops, best of 3: 0.212 usec per loop

আসুন সংস্করণগুলির মধ্যে পার্থক্যটি ব্যাখ্যা করুন। আমি সংকলিত কোড পরীক্ষা করব।

পাইথন 3 এর জন্য:

import dis

def list_iterate():
    [item for item in ["a", "b", "c"]]

dis.dis(list_iterate)
#>>>   4           0 LOAD_CONST               1 (<code object <listcomp> at 0x7f4d06b118a0, file "", line 4>)
#>>>               3 LOAD_CONST               2 ('list_iterate.<locals>.<listcomp>')
#>>>               6 MAKE_FUNCTION            0
#>>>               9 LOAD_CONST               3 ('a')
#>>>              12 LOAD_CONST               4 ('b')
#>>>              15 LOAD_CONST               5 ('c')
#>>>              18 BUILD_LIST               3
#>>>              21 GET_ITER
#>>>              22 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
#>>>              25 POP_TOP
#>>>              26 LOAD_CONST               0 (None)
#>>>              29 RETURN_VALUE

def string_iterate():
    [item for item in "abc"]

dis.dis(string_iterate)
#>>>  21           0 LOAD_CONST               1 (<code object <listcomp> at 0x7f4d06b17150, file "", line 21>)
#>>>               3 LOAD_CONST               2 ('string_iterate.<locals>.<listcomp>')
#>>>               6 MAKE_FUNCTION            0
#>>>               9 LOAD_CONST               3 ('abc')
#>>>              12 GET_ITER
#>>>              13 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
#>>>              16 POP_TOP
#>>>              17 LOAD_CONST               0 (None)
#>>>              20 RETURN_VALUE

আপনি এখানে দেখুন যে তালিকাটি প্রতিবার তালিকা তৈরির কারণে ধীর হয়ে যাওয়ার সম্ভাবনা রয়েছে।

এই

 9 LOAD_CONST   3 ('a')
12 LOAD_CONST   4 ('b')
15 LOAD_CONST   5 ('c')
18 BUILD_LIST   3

অংশ। স্ট্রিং ভেরিয়েন্টটিতে কেবল রয়েছে

 9 LOAD_CONST   3 ('abc')

আপনি এটি পরীক্ষা করতে পারেন যে এটি কোনও পার্থক্য বলে মনে হচ্ছে:

def string_iterate():
    [item for item in ("a", "b", "c")]

dis.dis(string_iterate)
#>>>  35           0 LOAD_CONST               1 (<code object <listcomp> at 0x7f4d068be660, file "", line 35>)
#>>>               3 LOAD_CONST               2 ('string_iterate.<locals>.<listcomp>')
#>>>               6 MAKE_FUNCTION            0
#>>>               9 LOAD_CONST               6 (('a', 'b', 'c'))
#>>>              12 GET_ITER
#>>>              13 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
#>>>              16 POP_TOP
#>>>              17 LOAD_CONST               0 (None)
#>>>              20 RETURN_VALUE

এটি কেবল উত্পাদন করে

 9 LOAD_CONST               6 (('a', 'b', 'c'))

হিসাবে tuples অপরিবর্তনীয়। টেস্ট:

>>> python3 -m timeit '[x for x in ("a", "b", "c")]'
1000000 loops, best of 3: 0.369 usec per loop

দুর্দান্ত, গতিতে ব্যাক আপ।

পাইথন 2 এর জন্য:

def list_iterate():
    [item for item in ["a", "b", "c"]]

dis.dis(list_iterate)
#>>>   2           0 BUILD_LIST               0
#>>>               3 LOAD_CONST               1 ('a')
#>>>               6 LOAD_CONST               2 ('b')
#>>>               9 LOAD_CONST               3 ('c')
#>>>              12 BUILD_LIST               3
#>>>              15 GET_ITER            
#>>>         >>   16 FOR_ITER                12 (to 31)
#>>>              19 STORE_FAST               0 (item)
#>>>              22 LOAD_FAST                0 (item)
#>>>              25 LIST_APPEND              2
#>>>              28 JUMP_ABSOLUTE           16
#>>>         >>   31 POP_TOP             
#>>>              32 LOAD_CONST               0 (None)
#>>>              35 RETURN_VALUE        

def string_iterate():
    [item for item in "abc"]

dis.dis(string_iterate)
#>>>   2           0 BUILD_LIST               0
#>>>               3 LOAD_CONST               1 ('abc')
#>>>               6 GET_ITER            
#>>>         >>    7 FOR_ITER                12 (to 22)
#>>>              10 STORE_FAST               0 (item)
#>>>              13 LOAD_FAST                0 (item)
#>>>              16 LIST_APPEND              2
#>>>              19 JUMP_ABSOLUTE            7
#>>>         >>   22 POP_TOP             
#>>>              23 LOAD_CONST               0 (None)
#>>>              26 RETURN_VALUE        

বিজোড় বিষয়টি হল আমাদের তালিকার একই বিল্ডিং রয়েছে তবে এটি এখনও এটির জন্য দ্রুত। পাইথন 2 অদ্ভুত দ্রুত অভিনয় করছে।

আসুন বুঝতে এবং পুনরায় সময় মুছে ফেলা যাক। _ =এটা অপ্টিমাইজ পেয়ে প্রতিরোধ করা হয়।

>>> python3 -m timeit '_ = ["a", "b", "c"]'
10000000 loops, best of 3: 0.0707 usec per loop

>>> python3 -m timeit '_ = "abc"'
100000000 loops, best of 3: 0.0171 usec per loop

আমরা দেখতে পাচ্ছি যে সংস্করণগুলির মধ্যে পার্থক্য (সেই সংখ্যাগুলি ছোট) এর জন্য অ্যাকাউন্টিং করার জন্য সূচনাটি যথেষ্ট তাত্পর্যপূর্ণ নয়! আমরা এভাবে উপসংহারে পৌঁছাতে পারি যে পাইথন 3 এর ধীরে ধীরে উপলব্ধি রয়েছে। এটি পাইথন 3 হিসাবে নিরাপদ স্কোপিংয়ের জন্য উপলব্ধিগুলি পরিবর্তন করে sense

ঠিক আছে, এখন মানদণ্ডকে উন্নত করুন (আমি কেবল ওভারহেড অপসারণ করছি যা পুনরাবৃত্তি নয়)। এটি পূর্বে বরাদ্দের মাধ্যমে পুনরাবৃত্তের বিল্ডিং সরিয়ে দেয়:

>>> python3 -m timeit -s 'iterable = "abc"'           '[x for x in iterable]'
1000000 loops, best of 3: 0.387 usec per loop

>>> python3 -m timeit -s 'iterable = ["a", "b", "c"]' '[x for x in iterable]'
1000000 loops, best of 3: 0.368 usec per loop
>>> python2 -m timeit -s 'iterable = "abc"'           '[x for x in iterable]'
1000000 loops, best of 3: 0.309 usec per loop

>>> python2 -m timeit -s 'iterable = ["a", "b", "c"]' '[x for x in iterable]'
10000000 loops, best of 3: 0.164 usec per loop

কল iterকরা ওভারহেড কিনা তা আমরা পরীক্ষা করতে পারি :

>>> python3 -m timeit -s 'iterable = "abc"'           'iter(iterable)'
10000000 loops, best of 3: 0.099 usec per loop

>>> python3 -m timeit -s 'iterable = ["a", "b", "c"]' 'iter(iterable)'
10000000 loops, best of 3: 0.1 usec per loop
>>> python2 -m timeit -s 'iterable = "abc"'           'iter(iterable)'
10000000 loops, best of 3: 0.0913 usec per loop

>>> python2 -m timeit -s 'iterable = ["a", "b", "c"]' 'iter(iterable)'
10000000 loops, best of 3: 0.0854 usec per loop

না, না। পার্থক্যটি খুব সামান্য, বিশেষত পাইথন 3 এর জন্য।

তাহলে আসুন আরও বেশি অযাচিত ওভারহেড সরিয়ে ফেলা যাক ... পুরো জিনিসটি ধীর করে দিয়ে! উদ্দেশ্যটি কেবল দীর্ঘতর পুনরাবৃত্তি হওয়া যাতে সময় ওভারহেড লুকায়।

>>> python3 -m timeit -s 'import random; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' '[x for x in iterable]'
100 loops, best of 3: 3.12 msec per loop

>>> python3 -m timeit -s 'import random; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' '[x for x in iterable]'
100 loops, best of 3: 2.77 msec per loop
>>> python2 -m timeit -s 'import random; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' '[x for x in iterable]'
100 loops, best of 3: 2.32 msec per loop

>>> python2 -m timeit -s 'import random; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' '[x for x in iterable]'
100 loops, best of 3: 2.09 msec per loop

এটি আসলে খুব বেশি পরিবর্তন হয়নি , তবে এটি কিছুটা সাহায্য করেছে।

সুতরাং বোধগম্যতা সরান। এটি ওভারহেড যা প্রশ্নের অংশ নয়:

>>> python3 -m timeit -s 'import random; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' 'for x in iterable: pass'
1000 loops, best of 3: 1.71 msec per loop

>>> python3 -m timeit -s 'import random; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'for x in iterable: pass'
1000 loops, best of 3: 1.36 msec per loop
>>> python2 -m timeit -s 'import random; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' 'for x in iterable: pass'
1000 loops, best of 3: 1.27 msec per loop

>>> python2 -m timeit -s 'import random; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'for x in iterable: pass'
1000 loops, best of 3: 935 usec per loop

ওটা অনেকটা এটার মতই! dequeপুনরাবৃত্তি করতে ব্যবহার করে আমরা কিছুটা দ্রুত এগিয়ে যেতে পারি । এটি মূলত একই, তবে এটি দ্রুত :

>>> python3 -m timeit -s 'import random; from collections import deque; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' 'deque(iterable, maxlen=0)'
1000 loops, best of 3: 777 usec per loop

>>> python3 -m timeit -s 'import random; from collections import deque; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'deque(iterable, maxlen=0)'
1000 loops, best of 3: 405 usec per loop
>>> python2 -m timeit -s 'import random; from collections import deque; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' 'deque(iterable, maxlen=0)'
1000 loops, best of 3: 805 usec per loop

>>> python2 -m timeit -s 'import random; from collections import deque; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'deque(iterable, maxlen=0)'
1000 loops, best of 3: 438 usec per loop

আমাকে যেটা প্রভাবিত করেছে তা হ'ল ইউনিকোড বাইটস্ট্রিংয়ের সাথে প্রতিযোগিতামূলক। আমরা চেষ্টা করে bytesএবং unicodeউভয়েই স্পষ্ট করে এটি পরীক্ষা করতে পারি :

  • bytes

    >>> python3 -m timeit -s 'import random; from collections import deque; iterable = b"".join(chr(random.randint(0, 127)).encode("ascii") for _ in range(100000))' 'deque(iterable, maxlen=0)'                                                                    :(
    1000 loops, best of 3: 571 usec per loop
    
    >>> python3 -m timeit -s 'import random; from collections import deque; iterable =         [chr(random.randint(0, 127)).encode("ascii") for _ in range(100000)]' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 394 usec per loop
    >>> python2 -m timeit -s 'import random; from collections import deque; iterable = b"".join(chr(random.randint(0, 127))                 for _ in range(100000))' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 757 usec per loop
    
    >>> python2 -m timeit -s 'import random; from collections import deque; iterable =         [chr(random.randint(0, 127))                 for _ in range(100000)]' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 438 usec per loop

    এখানে আপনি পাইথন 3 দেখতে পাইথন 2 এর চেয়ে আসলে দ্রুত

  • unicode

    >>> python3 -m timeit -s 'import random; from collections import deque; iterable = u"".join(   chr(random.randint(0, 127)) for _ in range(100000))' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 800 usec per loop
    
    >>> python3 -m timeit -s 'import random; from collections import deque; iterable =         [   chr(random.randint(0, 127)) for _ in range(100000)]' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 394 usec per loop
    >>> python2 -m timeit -s 'import random; from collections import deque; iterable = u"".join(unichr(random.randint(0, 127)) for _ in range(100000))' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 1.07 msec per loop
    
    >>> python2 -m timeit -s 'import random; from collections import deque; iterable =         [unichr(random.randint(0, 127)) for _ in range(100000)]' 'deque(iterable, maxlen=0)'
    1000 loops, best of 3: 469 usec per loop

    আবার পাইথন 3 দ্রুততর, যদিও এটি প্রত্যাশিত strছিল ( পাইথন 3 তে তার বেশ মনোযোগ ছিল)।

আসলে, এটি unicode- bytesপার্থক্যটি খুব সামান্য, যা চিত্তাকর্ষক।

সুতরাং আসুন এটি একটি ক্ষেত্রে বিশ্লেষণ করি, কারণ এটি আমার পক্ষে দ্রুত এবং সুবিধাজনক:

>>> python3 -m timeit -s 'import random; from collections import deque; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' 'deque(iterable, maxlen=0)'
1000 loops, best of 3: 777 usec per loop

>>> python3 -m timeit -s 'import random; from collections import deque; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'deque(iterable, maxlen=0)'
1000 loops, best of 3: 405 usec per loop

আমরা আসলে টিম পিটারের 10-বার-উত্তোলিত উত্তরটি বাতিল করতে পারি!

>>> foo = iterable[123]
>>> iterable[36] is foo
True

এগুলো নতুন জিনিস নয়!

তবে এটি উল্লেখযোগ্য: ইনডেক্সিং ব্যয় । পার্থক্যটি সম্ভবত সূচকের মধ্যে থাকবে, সুতরাং পুনরাবৃত্তিটি সরিয়ে ফেলুন এবং কেবল সূচক:

>>> python3 -m timeit -s 'import random; iterable = "".join(chr(random.randint(0, 127)) for _ in range(100000))' 'iterable[123]'
10000000 loops, best of 3: 0.0397 usec per loop

>>> python3 -m timeit -s 'import random; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'iterable[123]'
10000000 loops, best of 3: 0.0374 usec per loop

পার্থক্যটি সামান্য বলে মনে হচ্ছে, তবে ব্যয়ের অন্তত অর্ধেক ওভারহেড:

>>> python3 -m timeit -s 'import random; iterable =        [chr(random.randint(0, 127)) for _ in range(100000)]' 'iterable; 123'
100000000 loops, best of 3: 0.0173 usec per loop

সুতরাং গতির পার্থক্য এটি দোষী করার সিদ্ধান্ত নিতে যথেষ্ট। আমি মনে করি.

তাহলে এত দ্রুত তালিকাকে কেন তালিকাভুক্ত করা হচ্ছে?

ঠিক আছে, আমি এটিতে আপনার কাছে ফিরে আসব, তবে আমার অনুমান যে এটি অভ্যন্তরীণ স্ট্রিংগুলি (বা ক্যাশেড অক্ষরগুলি এটি একটি পৃথক প্রক্রিয়াধীন হলে) পরীক্ষা করতে পারে to এটি সর্বোত্তমের চেয়ে কম দ্রুত হবে। তবে আমি উত্স পরীক্ষা করতে যাব (যদিও আমি সি ... এ স্বাচ্ছন্দ্যবোধ করি না)) :)।


সুতরাং এখানে উত্স:

static PyObject *
unicode_getitem(PyObject *self, Py_ssize_t index)
{
    void *data;
    enum PyUnicode_Kind kind;
    Py_UCS4 ch;
    PyObject *res;

    if (!PyUnicode_Check(self) || PyUnicode_READY(self) == -1) {
        PyErr_BadArgument();
        return NULL;
    }
    if (index < 0 || index >= PyUnicode_GET_LENGTH(self)) {
        PyErr_SetString(PyExc_IndexError, "string index out of range");
        return NULL;
    }
    kind = PyUnicode_KIND(self);
    data = PyUnicode_DATA(self);
    ch = PyUnicode_READ(kind, data, index);
    if (ch < 256)
        return get_latin1_char(ch);

    res = PyUnicode_New(1, ch);
    if (res == NULL)
        return NULL;
    kind = PyUnicode_KIND(res);
    data = PyUnicode_DATA(res);
    PyUnicode_WRITE(kind, data, 0, ch);
    assert(_PyUnicode_CheckConsistency(res, 1));
    return res;
}

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

ch = PyUnicode_READ(kind, data, index);

তবে আমরা আশা করব যে এটি দ্রুত, কারণ আমরা এটি একটি সূচকযুক্ত সি অ্যারে থেকে সূচকে পড়ছি। ফলাফলটি, ch256 এরও কম হবে সুতরাং আমরা ক্যাশেড চরিত্রটিকে ফিরিয়ে দেব get_latin1_char(ch)

সুতরাং আমরা চালাব (প্রথম চেকগুলি বাদ দিচ্ছি)

kind = PyUnicode_KIND(self);
data = PyUnicode_DATA(self);
ch = PyUnicode_READ(kind, data, index);
return get_latin1_char(ch);

কোথায়

#define PyUnicode_KIND(op) \
    (assert(PyUnicode_Check(op)), \
     assert(PyUnicode_IS_READY(op)),            \
     ((PyASCIIObject *)(op))->state.kind)

(যা উদ্বেগজনক কারণ ডিবাগগুলিতে দৃser়তাগুলি অবহেলিত হয়ে পড়েছে [যাতে আমি তা পরীক্ষা করে দেখতে পারি যে তারা দ্রুত)] এবং ((PyASCIIObject *)(op))->state.kind)এটি (আমার মনে হয়) ইন্ডিয়ারেশন এবং সি-লেভেল কাস্ট);

#define PyUnicode_DATA(op) \
    (assert(PyUnicode_Check(op)), \
     PyUnicode_IS_COMPACT(op) ? _PyUnicode_COMPACT_DATA(op) :   \
     _PyUnicode_NONCOMPACT_DATA(op))

(যা ম্যাক্রোগুলি ( Something_CAPITALIZED) সমস্ত দ্রুত) ধরে রেখে অনুরূপ কারণে বিরক্তিকর হয়),

#define PyUnicode_READ(kind, data, index) \
    ((Py_UCS4) \
    ((kind) == PyUnicode_1BYTE_KIND ? \
        ((const Py_UCS1 *)(data))[(index)] : \
        ((kind) == PyUnicode_2BYTE_KIND ? \
            ((const Py_UCS2 *)(data))[(index)] : \
            ((const Py_UCS4 *)(data))[(index)] \
        ) \
    ))

(যার মধ্যে সূচকগুলি জড়িত তবে আসলেই ধীর হয় না) এবং

static PyObject*
get_latin1_char(unsigned char ch)
{
    PyObject *unicode = unicode_latin1[ch];
    if (!unicode) {
        unicode = PyUnicode_New(1, ch);
        if (!unicode)
            return NULL;
        PyUnicode_1BYTE_DATA(unicode)[0] = ch;
        assert(_PyUnicode_CheckConsistency(unicode, 1));
        unicode_latin1[ch] = unicode;
    }
    Py_INCREF(unicode);
    return unicode;
}

যা আমার সন্দেহকে নিশ্চিত করে যে:

  • এটি ক্যাশেড:

    PyObject *unicode = unicode_latin1[ch];
  • এটি দ্রুত হওয়া উচিত। if (!unicode)চলবে না হয়, তাই এটি এই ক্ষেত্রে আক্ষরিক সমতুল্য

    PyObject *unicode = unicode_latin1[ch];
    Py_INCREF(unicode);
    return unicode;

সত্যই, পরীক্ষাগুলি assertদ্রুত হওয়ার পরে (এগুলি অক্ষম করে [আমার মনে হয় এটি সি-লেভেল জোর দিয়ে কাজ করে ...]), কেবলমাত্র ধীর-ধীর অংশগুলি হ'ল:

PyUnicode_IS_COMPACT(op)
_PyUnicode_COMPACT_DATA(op)
_PyUnicode_NONCOMPACT_DATA(op)

কোনটি:

#define PyUnicode_IS_COMPACT(op) \
    (((PyASCIIObject*)(op))->state.compact)

(দ্রুত, আগের মত),

#define _PyUnicode_COMPACT_DATA(op)                     \
    (PyUnicode_IS_ASCII(op) ?                   \
     ((void*)((PyASCIIObject*)(op) + 1)) :              \
     ((void*)((PyCompactUnicodeObject*)(op) + 1)))

(ম্যাক্রো দ্রুত হলে IS_ASCIIদ্রুত), এবং

#define _PyUnicode_NONCOMPACT_DATA(op)                  \
    (assert(((PyUnicodeObject*)(op))->data.any),        \
     ((((PyUnicodeObject *)(op))->data.any)))

(দ্রুত হিসাবে এটি একটি প্রতিস্থাপন প্লাস একটি ইন্ডিরিশন প্লাস একটি কাস্ট)।

সুতরাং আমরা নীচে (খরগোশের গর্ত) এর নিচে:

PyUnicode_IS_ASCII

যা হলো

#define PyUnicode_IS_ASCII(op)                   \
    (assert(PyUnicode_Check(op)),                \
     assert(PyUnicode_IS_READY(op)),             \
     ((PyASCIIObject*)op)->state.ascii)

হুম ... খুব দ্রুত মনে হচ্ছে ...


ঠিক আছে, তবে এর সাথে এর তুলনা করা যাক PyList_GetItem। (হ্যাঁ, আমাকে আরও কাজ দেওয়ার জন্য টিম পিটার্সকে ধন্যবাদ : পি।)

PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
    if (!PyList_Check(op)) {
        PyErr_BadInternalCall();
        return NULL;
    }
    if (i < 0 || i >= Py_SIZE(op)) {
        if (indexerr == NULL) {
            indexerr = PyUnicode_FromString(
                "list index out of range");
            if (indexerr == NULL)
                return NULL;
        }
        PyErr_SetObject(PyExc_IndexError, indexerr);
        return NULL;
    }
    return ((PyListObject *)op) -> ob_item[i];
}

আমরা দেখতে পাচ্ছি যে অ-ত্রুটিযুক্ত মামলায় এটি কেবল চলতে চলেছে:

PyList_Check(op)
Py_SIZE(op)
((PyListObject *)op) -> ob_item[i]

কোথায় PyList_Checkআছে

#define PyList_Check(op) \
     PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS)

( ট্যাবস! ট্যাবস !!! ) ( ইস্যু 21587 ) এটি 5 মিনিটের মধ্যে সংশোধন হয়ে একীভূত হয়ে গেছে । যেমন ... হ্যাঁ অভিশাপ। তারা স্কিটকে লজ্জায় ফেলেছিল।

#define Py_SIZE(ob)             (((PyVarObject*)(ob))->ob_size)
#define PyType_FastSubclass(t,f)  PyType_HasFeature(t,f)
#ifdef Py_LIMITED_API
#define PyType_HasFeature(t,f)  ((PyType_GetFlags(t) & (f)) != 0)
#else
#define PyType_HasFeature(t,f)  (((t)->tp_flags & (f)) != 0)
#endif

সুতরাং এটি সাধারণত সত্যই তুচ্ছ (দুটি ইন্ডিয়ারেশন এবং কয়েক বুলিয়ান চেক) যদি না Py_LIMITED_APIথাকে তবে কোন ক্ষেত্রে ... ???

তারপরে ইনডেক্সিং এবং একটি কাস্ট আছে ( ((PyListObject *)op) -> ob_item[i]) এবং আমরা সম্পন্ন করেছি।

সুতরাং তালিকার জন্য অবশ্যই চেক কম রয়েছে এবং ছোট গতির পার্থক্য অবশ্যই বোঝায় যে এটি প্রাসঙ্গিক হতে পারে।


আমি সাধারণভাবে মনে করি, (->)ইউনিকোডের জন্য আরও আরও টাইপ-চেকিং এবং ইন্ডিরিয়ারেশন রয়েছে। মনে হচ্ছে আমি একটা পয়েন্ট মিস করছি, তবে কী ?


17
আপনি কোডটি স্ব-বর্ণনামূলক হিসাবে উপস্থাপন করছেন; এমনকি স্নিপেটগুলি উপসংহার হিসাবে উপস্থাপন করছেন। দুর্ভাগ্যক্রমে আমার জন্য, আমি সত্যিই এটি অনুসরণ করতে পারি না। যা ভুল তা খুঁজে বের করার আপনার দৃষ্টিভঙ্গি শক্ত নয়, তা বলছেন না তবে এটি অনুসরণ করা যদি আরও সহজ হত তবে ভাল হবে।
পাসক্যালভিকুটেন

2
আমি এটির উন্নতি করার চেষ্টা করেছি, তবে কীভাবে এটি আরও পরিষ্কার করা যায় তা সম্পর্কে আমি নিশ্চিত নই। মনে রাখবেন যে আমি সি লিখছি না, সুতরাং এটি কোডের একটি উচ্চ-স্তরের বিশ্লেষণ এবং কেবলমাত্র সামগ্রিক ধারণাটি গুরুত্বপূর্ণ।
Veedrac

@Nit আমি যুক্ত করেছি যদি অভাব বোধ হয় তবে বলুন। দুর্ভাগ্যক্রমে এটি হাইলাইট করে যে আমি আসলে উত্তরটি জানি না (* হাঁসফাঁস *)।
Veedrac

3
আমি আপনার উত্তরটি গ্রহণ করার আগেই আমি এটি অন্য এক দিন দেব (আমি আরও কিছু কংক্রিটের পপ আপ দেখতে পছন্দ করব), তবে খুব আকর্ষণীয় এবং ভাল গবেষণার উত্তরের জন্য আপনাকে ধন্যবাদ।
সানজয় ভার্মা

4
নোট করুন যে আপনি একটি চলন্ত লক্ষ্যে শ্যুট করছেন ;-) এই প্রয়োগটি পাইথন 2 এবং পাইথন 3 এর মধ্যে কেবল আলাদা নয়, তবে বিভিন্ন প্রকাশের মধ্যেও রয়েছে। উদাহরণস্বরূপ, বর্তমান বিকাশের ট্রাঙ্কে, get_latin1_char()কৌশলটি আর বিদ্যমান নেই unicode_getitem(), তবে নিম্ন স্তরের unicode_char। সুতরাং এখন অন্য স্তর ফাংশন কল আছে - বা না (ব্যবহৃত সংকলক এবং অপ্টিমাইজেশন পতাকা উপর নির্ভর করে)। বিশদের এই স্তরে, কেবল কোনও নির্ভরযোগ্য উত্তর নেই ;-)
টিম পিটারস

31

যখন তোমাদের উপর সবচেয়ে ধারক বস্তু পুনরুক্তি (তালিকা, tuples, dicts, ...), পুনরুক্তিকারীর বস্তু বিতরণ করে ধারক।

কিন্তু আপনি যখন স্ট্রিং দিয়ে পুনরাবৃত্তি করেন, তখন প্রতিটি বিতরণ করা অক্ষরের জন্য একটি নতুন অবজেক্ট তৈরি করতে হয় - স্ট্রিং একই অর্থে কোনও "ধারক" নয় তালিকাকে একটি ধারক বলে। স্ট্রিংয়ের পৃথক অক্ষরগুলি পুনরাবৃত্তির মাধ্যমে এই বস্তুগুলি তৈরি করার আগে স্বতন্ত্র বস্তু হিসাবে উপস্থিত থাকে না।


3
আমি আসলে এটি সত্য বলে মনে করি না। আপনি চেক করতে পারেন is। এটা ঠিক শোনাচ্ছে , তবে আমি সত্যিই এটি হতে পারে বলে মনে করি না।
Veedrac

@ ভাইড্রাক উত্তরটি একবার দেখুন।
খ্রিস্টান

3
stringobject.cদেখায় যে __getitem__স্ট্রিংগুলির জন্য সঞ্চিত 1-অক্ষরের স্ট্রিংয়ের সারণি থেকে ফলাফলটি পুনরুদ্ধার করা হয়, সুতরাং তাদের জন্য বরাদ্দ ব্যয় কেবল একবারই ব্যয় করা হয়।
ব্যবহারকারী 2357112 26:57

10
@ ব্যবহারকারী 2357112, হ্যাঁ, পাইথন 2 এ সরল স্ট্রিংয়ের জন্য এটি একটি গুরুত্বপূর্ণ বিষয়। পাইথন 3-এ, সমস্ত স্ট্রিং ইউনিকোডকে "অফিশিয়ালি" এবং আরও অনেকগুলি বিবরণ জড়িত (বীড্রাকের উত্তর দেখুন)। উদাহরণস্বরূপ, পাইথন 3-এ, পরে s = chr(256), s is chr(256)প্রত্যাবর্তন False- একমাত্র প্রকারটি জানা যথেষ্ট নয়, কারণ বিশেষ মূল্যগুলির ofিবিগুলি ডেটা মানগুলিকে ট্রিগার করে এমন কভারগুলির আওতায় বিদ্যমান ।
টিম পিটার্স

1

স্ট্রিংয়ের জন্য পুনরাবৃত্তি তৈরি করার জন্য আপনি ব্যয় করতে এবং ওভারহেড হতে পারেন। অ্যারে ইতিমধ্যে ইনস্ট্যান্টেশন উপর একটি পুনরাবৃত্তি রয়েছে।

সম্পাদনা করুন:

>>> timeit("[x for x in ['a','b','c']]")
0.3818681240081787
>>> timeit("[x for x in 'abc']")
0.3732869625091553

এটি 2.7 ব্যবহার করে চালানো হয়েছিল তবে আমার ম্যাক বই প্রো i7 এ on এটি সিস্টেম কনফিগারেশন পার্থক্যের ফলাফল হতে পারে।


এমনকি কেবল সোজা পুনরুক্তি ব্যবহার করেও স্ট্রিংটি উল্লেখযোগ্যভাবে ধীর হয়। সময়কাল ("[এতে x এর জন্য]]", "এটি = ইটার ('অ্যাবসি')") = 0.34543599384033535; সময়কাল ("[এতে x এর জন্য]]", "এটি = ইটার (তালিকা ('অ্যাবসি'))") = 0.2791691380446508
সানজয় ভার্মা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.