Grep इतनी तेजी से कैसे चलता है?


113

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

कहा जा रहा है कि मैं यह पता लगाने में सक्षम नहीं हूं कि यह कैसे हो रहा है? वेब पर बहुत अधिक उपलब्ध नहीं है।

क्या कोई मुझे इस बारे में सहायता कर सकता है?


5
यह खुला स्रोत है ताकि आप अपने लिए एक नज़र रख सकें। gnu.org/software/grep/devel.html
driis

6
हास्यास्पद मछली के पास एक बढ़िया राइटअप है जो आपके प्रश्न का उत्तर दे रहा है: ridiculousfish.com/blog/posts/old-age-and-treachery.html
David Wolever

@WilliamPursell जब निष्पादन का समय सेकंड में हो जाता है, तो JIT शायद गर्म हो गई है और दिमाग सुन्न होने के कारण (1) grep अविश्वसनीय रूप से स्मार्ट हो जाता है कि वह क्या करता है और (2) जावा कोड एक बहुत सुंदर एल्गोरिथ्म विकल्प बनाता है विशिष्ट समस्या के लिए grep पर केंद्रित है।

3
आपका जावा कार्यान्वयन जेवीएम को शुरू करने में कितना समय खर्च करता है, और वास्तव में आपके कोड को निष्पादित करने में कितना समय लगता है? या यह आपके जावा कोड में उपयोग किए गए एल्गोरिदम की बात हो सकती है; O (N ^ 2) एल्गोरिथ्म किसी भी भाषा में धीमा होने की संभावना है।
कीथ थॉम्पसन

जवाबों:


169

अपने प्रश्न को मानते हुए GNU grepविशेष रूप से। लेखक माइक हैर्टेल का एक नोट यहाँ है:

GNU grep तेज़ है क्योंकि यह हर बार BYTE पर नज़र रखता है।

क्योंकि यह प्रत्येक बाइट कि इसके लिए कार्यान्वित बहुत कुछ निर्देश जीएनयू ग्रेप तेज है करता है पर देखो।

GNU grep जाने-माने बॉयर-मूर एल्गोरिथ्म का उपयोग करता है, जो पहली बार लक्ष्य स्ट्रिंग के अंतिम अक्षर के लिए दिखता है, और यह देखने के लिए एक लुकअप तालिका का उपयोग करता है कि यह बताए कि जब भी यह गैर-मिलान वर्ण पाता है तो इनपुट में इसे कितना आगे छोड़ सकता है।

GNU grep, Boyer-Moore के आंतरिक लूप को भी अनियंत्रित करता है, और Boyer-Moore डेल्टा तालिका प्रविष्टियों को इस तरह सेट करता है कि उसे हर अनियंत्रित कदम पर लूप निकास परीक्षण करने की आवश्यकता नहीं होती है। इसका परिणाम यह है कि, सीमा में, GNU grep का औसत 3 x86 निर्देशों से कम है जो प्रत्येक इनपुट बाइट के लिए निष्पादित होता है, यह वास्तव में दिखता है (और यह पूरी तरह से कई बाइट्स को छोड़ देता है)।

GNU grep कच्चे यूनिक्स इनपुट सिस्टम कॉल का उपयोग करता है और इसे पढ़ने के बाद डेटा को कॉपी करने से बचता है। इसके अलावा, GNU grep AVOIDS INPUT लाइनों में ले जा रहा है। नई सुर्खियों की तलाश कई बार एक कारक के कारण धीमी हो जाएगी, क्योंकि नई सूचियों को खोजने के लिए इसे हर बाइट को देखना होगा!

इसलिए लाइन-ओरिएंटेड इनपुट का उपयोग करने के बजाय, GNU grep एक बड़े बफर में कच्चे डेटा को पढ़ता है, बॉयर-मूर का उपयोग करके बफर को खोजता है, और केवल जब यह एक मैच पाता है तो यह बाउंडिंग नईलाइन्स (कुछ कमांड लाइन विकल्प) की तलाश करता है n इस अनुकूलन को अक्षम करें।)

यह उत्तर यहां से ली गई जानकारी का एक सबसेट है


41

स्टीव के उत्कृष्ट उत्तर में जोड़ने के लिए।

यह व्यापक रूप से ज्ञात नहीं हो सकता है, लेकिन संक्षिप्त समय की तुलना में लंबे पैटर्न-स्ट्रिंग के लिए grepping करते समय grep लगभग हमेशा तेज होता है , क्योंकि एक लंबे पैटर्न में, बोयर-मूर आगे की दिशा में आगे निकल सकता है ताकि बेहतर सबलाइन गति प्राप्त कर सके :

उदाहरण:

# after running these twice to ensure apples-to-apples comparison
# (everything is in the buffer cache) 

$ time grep -c 'tg=f_c' 20140910.log
28
0.168u 0.068s 0:00.26

$ time grep -c ' /cc/merchant.json tg=f_c' 20140910.log
28
0.100u 0.056s 0:00.17

लंबी फॉर्म 35% तेज है!

कैसे? बॉयर-मूर पैटर्न-स्ट्रिंग से एक स्किप-फॉरवर्ड तालिका को संरक्षित करता है, और जब भी कोई बेमेल होता है, तो यह स्किप तालिका में चार में इनपुट में किसी भी चार्ट की तुलना करने से पहले सबसे लंबे समय तक स्किप संभव (पिछले चार से पहली बार) को चुनता है।

यहाँ एक वीडियो है जिसमें बॉयर मूर (कोमराधोमेर को श्रेय) समझाता है

एक और आम गलतफहमी (GNU grep के लिए) की fgrepतुलना में तेज़ है grepfमें fgrepके लिए 'तेजी' खड़े नहीं करता है, यह तय '(आदमी पृष्ठ देखें) के लिए खड़ा है, और तब से दोनों एक ही कार्यक्रम है, और दोनों का उपयोग कर रहे हैं बोयर-मूर , उन दोनों के बीच की गति में कोई अंतर नहीं है जब फिक्स्ड के लिए खोज है regexp विशेष आकर्षण के बिना तार। एकमात्र कारण मैं उपयोग fgrepजब वहाँ एक regexp विशेष वर्ण है (जैसे ., [], या *) मैं नहीं चाहता कि यह इस तरह के रूप में व्याख्या की जा करना चाहते हैं। और फिर भी अधिक पोर्टेबल / मानक रूप grep -Fको पसंद किया जाता है fgrep


3
यह सहज है कि लंबे पैटर्न तेज हैं। यदि पैटर्न एक बाइट था तो grep को हर बाइट को जांचना होगा। यदि पैटर्न 4-बाइट्स है, तो यह 4-बाइट की खाल बना सकता है। यदि पैटर्न पाठ के रूप में लंबे समय तक था, तो grep केवल एक कदम करेगा।
नोएल

12
हां, यह सहज है - यदि आप समझते हैं कि बॉयर-मूर कैसे काम करते हैं।
अरीफ

2
अन्यथा यह सहज है। एक छोटे से एक घास का ढेर में एक लंबी सुई ढूंढना आसान होगा
रजत

2
"लंबे समय तक तेज़ रहने" का काउंटर उदाहरण ऐसे मामलों में होता है जहां आपको असफल होने से पहले बहुत सारे परीक्षण करने होते हैं, और आप किसी भी तरह आगे नहीं बढ़ सकते हैं। मान लें कि फ़ाइल xs.txtमें 100000000 'x है, और आप करते हैं grep yx xs.txt, तो यह वास्तव में यदि आप ऐसा करते हैं , तो जल्दी से एक मैच खोजने में विफल रहता है grep yxxxxxxxxxxxxxxxxxxx xs.txt। बोयर-मूर के लिए बोयर-मूर-हॉर्सपूल सुधार उस मामले में स्किप-फॉरवर्ड पर सुधार करता है, लेकिन संभवतः यह सामान्य मामले में केवल तीन मशीन निर्देश नहीं होने जा रहा है।
lrn

2
@ टीनो धन्यवाद हां, ऐसा लगता है कि grep/fgrep/egrepएक ही निष्पादन योग्य के लिए सभी हार्डलिंक्स होने के दिन (GNU) समाप्त हो गए हैं। वे और अन्य एक्सटेंशन जैसे कि z*grep bz*grepबर्तन जो मक्खी पर सड़ते हैं) अब चारों ओर छोटे खोल-आवरण हैं grep। एकल निष्पादन योग्य और शेल रैपर के बीच स्विच पर कुछ दिलचस्प ऐतिहासिक टिप्पणियां इस कमिट में पाई जा सकती हैं: git.savannah.gnu.org/cgit/grep.git/commit/…
arielf
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.