2015-08-10 5 views
55

हैरानी की बात है, मुझे लगता है startswithin की तुलना में धीमी है:स्ट्रिंग की शुरुआत धीमी गति से क्यों होती है?

In [10]: s="ABCD"*10 

In [11]: %timeit s.startswith("XYZ") 
1000000 loops, best of 3: 307 ns per loop 

In [12]: %timeit "XYZ" in s 
10000000 loops, best of 3: 81.7 ns per loop 

हम सभी जानते हैं, in आपरेशन पूरी स्ट्रिंग खोज करने के लिए की जरूरत है और startswith सिर्फ पहले कुछ वर्ण की जाँच करने की जरूरत है, तो startswith अधिक कुशल होना चाहिए ।

जब s काफी बड़ा है, startswith तेजी से होता है:

In [13]: s="ABCD"*200 

In [14]: %timeit s.startswith("XYZ") 
1000000 loops, best of 3: 306 ns per loop 

In [15]: %timeit "XYZ" in s 
1000000 loops, best of 3: 666 ns per loop 

तो ऐसा लगता है कि फोन करने startswith कुछ भूमि के ऊपर है जो इसे धीमा हो जाता है जब स्ट्रिंग छोटा है है।

और मैंने यह पता लगाने की कोशिश की कि startswith कॉल का ओवरहेड क्या है।

In [16]: f=s.startswith 

In [17]: %timeit f("XYZ") 
1000000 loops, best of 3: 270 ns per loop 

इसके अलावा, मैं एक की लागत का परीक्षण किया: - के रूप में इस answer में उल्लेख किया है - यहाँ हम startswith देख सकते हैं अभी भी धीमी

पहले, मैं एक f चर इस्तेमाल किया डॉट आपरेशन की लागत को कम करने के लिए खाली समारोह कॉल:

In [18]: def func(a): pass 

In [19]: %timeit func("XYZ") 
10000000 loops, best of 3: 106 ns per loop 

डॉट संचालन और समारोह कॉल की लागत के बावजूद, startswith के समय के बारे में (270-106) = 164ns है, लेकिन in आपरेशन तक केवल 81.7ns एसएस। ऐसा लगता है कि अभी भी startswith के लिए कुछ ओवरहेड हैं, वह क्या है?

startswith और __contains__ के बीच परीक्षा परिणाम के रूप में प्रहार और LVC ने सुझाव दिया जोड़ें:

In [28]: %timeit s.startswith("XYZ") 
1000000 loops, best of 3: 314 ns per loop 

In [29]: %timeit s.__contains__("XYZ") 
1000000 loops, best of 3: 192 ns per loop 
+1

अधिक समान परिणाम प्राप्त करने के लिए, आप कर सकते हैं .__ में __ ("XYZ") है क्योंकि यह 's.startswith (" XYZ ") के समान मार्ग लेगा (फिर 'in' ऑपरेटर का उपयोग करेगा सदस्य का उपयोग कम करें)। हालांकि, तब भी 'startwith' मेरे लिए धीमा है। – poke

+2

मुझे लगता है कि प्रदर्शन अंतर का शेष '__contains__' पूरी तरह से टाइप किया गया है * सी * में, जबकि 'startwith' वास्तविक तर्क पार्सिंग और सामान करता है (आप एक ट्यूपल भी पास कर सकते हैं)। – poke

+0

पाइथन का कौन सा संस्करण आप उपयोग कर रहे हैं? 3.4.3 पर, मुझे 's.startswith (" XYZ ") 'रिपोर्ट 153ns मिलती है, और' s .__ में __ ("XYZ")' रिपोर्ट 16 9ns होती है। जैसा कि @ पोक कहते हैं, 'इन' का उपयोग करके विधि कॉल की तुलना में पूरी तरह से अलग लुकअप नियमों का उपयोग किया जाएगा - इसे सी स्तर पर फ़ंक्शन पॉइंटर से सीधे देखा जा सकता है, जबकि विधि लुकअप दो शब्दकोश खोज करता है और * फिर * करना है एक पायथन-स्तर समारोह कॉल। उन चीजों को अलग-अलग समय पर आपको अंतर के कुछ * विचार दे सकते हैं, लेकिन यह आवश्यक नहीं है। आपकी संख्याओं पर, उन ओवरहेड दोनों को घटाना 'startwith' * नकारात्मक * के लिए समय बनाता है! – lvc

उत्तर

38

जैसा कि पहले ही, टिप्पणी में उल्लेख किया है, तो आप s.__contains__("XYZ") का उपयोग आप एक परिणाम है कि अधिक s.startswith("XYZ") के समान है, क्योंकि यह एक ही मार्ग लेने की जरूरत है: स्ट्रिंग वस्तु पर सदस्य देखने, एक समारोह कॉल के बाद। यह आमतौर पर कुछ हद तक महंगा होता है (पर्याप्त नहीं है कि आपको निश्चित रूप से चिंता करनी चाहिए)। दूसरी तरफ, जब आप "XYZ" in s करते हैं, तो पार्सर ऑपरेटर को व्याख्या करता है और सदस्य को __contains__ (या इसके पीछे कार्यान्वयन के लिए उपयोग को कम कर सकता है, क्योंकि __contains__ ही कार्यान्वयन तक पहुंचने का एक ही तरीका है)।

आप बाईटकोड को देखकर इस बारे में एक विचार प्राप्त कर सकते हैं:

>>> dis.dis('"XYZ" in s') 
    1   0 LOAD_CONST    0 ('XYZ') 
       3 LOAD_NAME    0 (s) 
       6 COMPARE_OP    6 (in) 
       9 RETURN_VALUE 
>>> dis.dis('s.__contains__("XYZ")') 
    1   0 LOAD_NAME    0 (s) 
       3 LOAD_ATTR    1 (__contains__) 
       6 LOAD_CONST    0 ('XYZ') 
       9 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      12 RETURN_VALUE 

तो s.startswith("XYZ") साथ s.__contains__("XYZ") की तुलना अपने उदाहरण स्ट्रिंग s के लिए एक अधिक समान परिणाम का उत्पादन करेगा, तथापि, startswith अभी भी धीमी हो जाएगा।

उस पर पहुंचने के लिए, आप दोनों के कार्यान्वयन की जांच कर सकते हैं।contains implementation के लिए देखना दिलचस्प है कि यह स्थिर रूप से टाइप किया गया है, और केवल यह मानता है कि तर्क एक यूनिकोड ऑब्जेक्ट है। तो यह काफी कुशल है।

startswith implementation हालांकि एक "गतिशील" पायथन विधि है जिसके लिए वास्तव में तर्कों का विश्लेषण करने के लिए कार्यान्वयन की आवश्यकता होती है। (मेरी टिप्पणी के साथ मेरे द्वारा छोटा,):

static PyObject * unicode_startswith(PyObject *self, PyObject *args) 
{ 
    // argument parsing 
    PyObject *subobj; 
    PyObject *substring; 
    Py_ssize_t start = 0; 
    Py_ssize_t end = PY_SSIZE_T_MAX; 
    int result; 
    if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end)) 
     return NULL; 

    // tuple handling 
    if (PyTuple_Check(subobj)) {} 

    // unicode conversion 
    substring = PyUnicode_FromObject(subobj); 
    if (substring == NULL) {} 

    // actual implementation 
    result = tailmatch(self, substring, start, end, -1); 
    Py_DECREF(substring); 
    if (result == -1) 
     return NULL; 
    return PyBool_FromLong(result); 
} 

यह संभवत: एक बड़ा कारण है कि startswith धीमी है है startswith भी एक तर्क है, जो विधि थोड़ा धीमा के पूरे स्टार्ट-अप बनाता है के रूप में एक टपल का समर्थन करता है तारों के लिए जिसके लिए contains इसकी सादगी के कारण तेज़ है।

+0

कार्यान्वयन के लिए लिंक – mounaim

+2

@mounaim को तोड़ दिया गया है जाहिर है, पायथन का Mercurial सर्वर बस कुछ सेकंड के लिए नीचे चला गया। यह फिर से काम कर रहा है। – poke

1

इसका कारण यह है str.startswith() से अधिक str.__contains__() करता है की संभावना है, और इसलिए भी क्योंकि मेरा मानना ​​है कि str.__contains__ सी में पूरी तरह से संचालित होता है, जबकि str.startswith() है पायथन प्रकार के साथ बातचीत करने के लिए। इसका हस्ताक्षर str.startswith(prefix[, start[, end]]) है, जहां उपसर्ग प्रयास करने के लिए तारों का एक टुपल हो सकता है।

+1

"और अधिक" अस्पष्ट है और शायद सच नहीं है। कुशलतापूर्वक एक सबस्ट्रिंग ढूँढना एक छोटी सी समस्या नहीं है। [एल्गोरिदम की सूची।] (Https://en.wikipedia.org/wiki/String_searching_algorithm#Single_pattern_algorithms)। इसके विपरीत, एक उपसर्ग खोजना काफी आसान है और सामान्य रूप से तेज़ है। –

+0

आप सही @PaulDraper हैं; मैं देर से था जब मैंने लिखा था: पी। और @LightnessRacesinOrbit संपादित करने के लिए धन्यवाद :)। – Cyphase

संबंधित मुद्दे