2009-02-24 14 views
5

के लिए अलग से रन बनाम मैं एक काफी बड़े स्ट्रिंग (~ 700k) जिसके खिलाफ मैं 10 regexes चलाने के लिए और regexes से किसी के सभी मैचों की गिनती करने की जरूरत मिल गया है। मेरा त्वरित और गंदे प्रत्यारोपण कुछ ऐसा करना था जैसे re.search ('(expr1) | (expr2) | ...'), लेकिन मैं सोच रहा था कि क्या हम इसके बजाय लूप में मिलान करके कोई प्रदर्शन लाभ देखेंगे:regex '|' ऑपरेटर प्रत्येक उप अभिव्यक्ति

दूसरे शब्दों में, मैं के प्रदर्शन की तुलना करना चाहते हैं: मैं आलसी जा रहा है बंद कर देंगे

def CountMatchesInBigstring(bigstring, my_regexes): 
    """Counts how many of the expressions in my_regexes match bigstring.""" 
    count = 0 
    combined_expr = '|'.join(['(%s)' % r for r in my_regexes]) 
    matches = re.search(combined_expr, bigstring) 
    if matches: 
    count += NumMatches(matches) 
    return count 

बनाम

def CountMatchesInBigstring(bigstring, my_regexes): 
    """Counts how many of the expressions in my_regexes match bigstring.""" 
    count = 0 
    for reg in my_regexes: 
    matches = re.search(reg, bigstring) 
    if matches: 
     count += NumMatches(matches) 
    return count 

और कुछ परीक्षण कल चलाने (और परिणाम पोस्ट), लेकिन मैंने सोचा क्या उत्तर किसी ऐसे व्यक्ति के लिए कूद जाएगा जो वास्तव में समझता है कि रीगेक्स कैसे काम करता है :)

उत्तर

1

मुझे संदेह है कि रेगेक्स भी वह करेगा जो आप करने की कोशिश कर रहे हैं ... केवल बहुत बेहतर :)

तो "|"

7

दो चीजें थोड़ा अलग परिणाम देगी, जब तक कि यह गारंटी न हो कि एक मैच एक और केवल एक रेगेक्स से मेल खाता है। अन्यथा अगर कुछ 2 मैच करता है तो इसे दो बार गिना जाएगा।

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

इसके अलावा, अगर यह एक बड़ी स्ट्रिंग (700k से भी बड़ा) वहाँ एक पास करने से लाभ हो सकता है, और इसलिए n कम स्मृति स्वैप का एक पहलू (डिस्क या CPU कैश करने के लिए) की जरूरत होगी थे।

मेरे शर्त अपने परीक्षण में है यह, हालांकि वास्तव में ध्यान देने योग्य नहीं है। मुझे वास्तविक परिणाम में दिलचस्पी है - कृपया परिणाम पोस्ट करें।

0

मैं amartynov से सहमत हूं लेकिन मैं यह जोड़ना चाहता हूं कि आप रेगेक्स पहले (re.compile()), esp संकलित करने पर भी विचार कर सकते हैं। दूसरे संस्करण में तब आप लूप में कुछ सेटअप समय बचा सकते हैं। हो सकता है कि आप इसे मापने के दौरान भी इसे माप सकें।

कारण मुझे लगता है कि एक शॉट बेहतर प्रदर्शन करता है कि मुझे लगता है कि यह पूरी तरह से सी स्पेस में किया गया है और इतना पाइथन कोड की व्याख्या करने की आवश्यकता नहीं है।

लेकिन संख्या की प्रतीक्षा कर रही।

+0

चूंकि उनके उदाहरणों में प्रत्येक रेगेक्स का उपयोग केवल एक बार किया जाता है, आपको पूर्व-संकलन द्वारा कोई प्रदर्शन सुधार नहीं करना चाहिए। –

+0

भले ही आपने प्रत्येक रेगेक्स को एक से अधिक बार इस्तेमाल किया हो, फिर भी पाइथन का री मॉड्यूल आपके लिए संकलित रेगेक्स को कैश करता है, इसलिए दूसरी बार में यह पूर्व-संकलित किसी भी तरह का उपयोग करेगा। – nosklo

0

एक संकलन और खोज को तेजी से परिणाम मिलना चाहिए, अभिव्यक्ति के निम्न स्तर पर लाभ लाभहीन हो सकता है लेकिन जितना अधिक आप अधिक लाभ के माध्यम से भाग लेते हैं। इसे एक बार संकलित करने और 10 बार संकलन और मिलान करने के साथ मिलान करने के बारे में सोचें।

1

मेरा मानना ​​है कि अपने पहले कार्यान्वयन किया जाएगा तेजी से:

  • अजगर प्रदर्शन के लिए महत्वपूर्ण सिद्धांतों में से एक "सी स्तर के लिए कदम तर्क" है - जिसका अर्थ है बिल्ट-इन कार्य (सी में लिखा) तेजी से कर रहे हैं शुद्ध पायथन कार्यान्वयन से। इसलिए, जब लूप को अंतर्निहित रेगेक्स मॉड्यूल द्वारा किया जाता है, तो यह तेज होना चाहिए
  • एक रेगेक्स एक पास में एकाधिक पैटेंस खोज सकता है, जिसका अर्थ है कि इसे केवल एक बार आपकी फ़ाइल सामग्री के माध्यम से चलाना होगा, जबकि एकाधिक रेगेक्स के पास होगा पूरी फाइल को कई बार पढ़ने के लिए।
5

यह समझने के लिए कि मॉड्यूल कैसे काम करता है - डीबग मोड में _sre.c संकलित करें (_sre.c में 103 लाइन पर VERBOSE परिभाषित करें और पाइथन recompile)। इसके बाद आप बीमार इस तरह कुछ देखते हैं:

 

>>> import re 
>>> p = re.compile('(a)|(b)|(c)') 
>>> p.search('a'); print '\n\n'; p.search('b') 
|0xb7f9ab10|(nil)|SEARCH 
prefix = (nil) 0 0 
charset = (nil) 
|0xb7f9ab1a|0xb7fb75f4|SEARCH 
|0xb7f9ab1a|0xb7fb75f4|ENTER 
allocating sre_match_context in 0 (32) 
allocate/grow stack 1064 
|0xb7f9ab1c|0xb7fb75f4|BRANCH 
allocating sre_match_context in 32 (32) 
|0xb7f9ab20|0xb7fb75f4|MARK 0 
|0xb7f9ab24|0xb7fb75f4|LITERAL 97 
|0xb7f9ab28|0xb7fb75f5|MARK 1 
|0xb7f9ab2c|0xb7fb75f5|JUMP 20 
|0xb7f9ab56|0xb7fb75f5|SUCCESS 
discard data from 32 (32) 
looking up sre_match_context at 0 
|0xb7f9ab1c|0xb7fb75f4|JUMP_BRANCH 
discard data from 0 (32) 
|0xb7f9ab10|0xb7fb75f5|END 




|0xb7f9ab10|(nil)|SEARCH 
prefix = (nil) 0 0 
charset = (nil) 
|0xb7f9ab1a|0xb7fb7614|SEARCH 
|0xb7f9ab1a|0xb7fb7614|ENTER 
allocating sre_match_context in 0 (32) 
allocate/grow stack 1064 
|0xb7f9ab1c|0xb7fb7614|BRANCH 
allocating sre_match_context in 32 (32) 
|0xb7f9ab20|0xb7fb7614|MARK 0 
|0xb7f9ab24|0xb7fb7614|LITERAL 97 
discard data from 32 (32) 
looking up sre_match_context at 0 
|0xb7f9ab1c|0xb7fb7614|JUMP_BRANCH 
allocating sre_match_context in 32 (32) 
|0xb7f9ab32|0xb7fb7614|MARK 2 
|0xb7f9ab36|0xb7fb7614|LITERAL 98 
|0xb7f9ab3a|0xb7fb7615|MARK 3 
|0xb7f9ab3e|0xb7fb7615|JUMP 11 
|0xb7f9ab56|0xb7fb7615|SUCCESS 
discard data from 32 (32) 
looking up sre_match_context at 0 
|0xb7f9ab2e|0xb7fb7614|JUMP_BRANCH 
discard data from 0 (32) 
|0xb7f9ab10|0xb7fb7615|END 

>>>          
 
+0

http://undv.python.org/view/python/trunk/Modules/_sre.c?view=markup – jfs

+0

में VERBOSE और VVERBOSE के लिए #define द्वारा #undf को बदलें। इसका अर्थ क्या है? – ThomasH

0

कम से कम गुजरता है: यह केवल अधिक स्मृति का उपयोग करेगा, जो आमतौर पर कोई मुद्दा नहीं है।

अगर किसी भी प्रकार को दुभाषिया को संभालने के लिए छोड़ा जा सकता है, तो यह सामान्य मानव समकक्ष की तुलना में हमेशा एक तेज समाधान (कार्यान्वित करने के लिए समय और निष्पादन के समय) को हमेशा मिलेगा।

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