यह जांचने के लिए सबसे तेज़ तरीका है कि क्या एक स्ट्रिंग माणिक में एक रेक्सएक्सपी से मेल खाती है?


96

अगर एक स्ट्रिंग रूबी में एक नियमित अभिव्यक्ति से मेल खाती है, तो यह जांचने का सबसे तेज़ तरीका क्या है?

मेरी समस्या यह है कि मुझे स्ट्रेंथ की एक विशाल सूची के माध्यम से "egrep" को ढूंढना है, जो कि एक regexp से मेल खाते हैं जो रनटाइम पर दिए गए हैं। मैं केवल इस बात की परवाह करता हूं कि स्ट्रिंग रेगेक्सपी से मेल खाती है, न कि जहां यह मेल खाता है, न ही मिलान समूहों की सामग्री क्या है। मुझे उम्मीद है कि इस धारणा का उपयोग मेरे कोड रेगेक्स को खर्च करने के समय को कम करने के लिए किया जा सकता है।

मैं regexp को लोड करता हूं

pattern = Regexp.new(ptx).freeze

मैंने पाया है कि string =~ patternकी तुलना में थोड़ा तेज है string.match(pattern)

क्या अन्य ट्रिक्स या शॉर्टकट हैं जो इस परीक्षण को और भी तेज करने के लिए उपयोग किए जा सकते हैं?


यदि आपको मेल खाने वाले समूहों की सामग्री की परवाह नहीं है, तो आपके पास उन्हें क्यों है? आप रेगेक्स को गैर-कैप्चरिंग में परिवर्तित करके तेजी से बना सकते हैं।
मार्क थॉमस

1
चूंकि regexp रन-टाइम पर प्रदान किया जाता है, इसलिए मुझे लगता है कि यह असंवैधानिक है, इस स्थिति में समूह के लिए reg-exp के भीतर आंतरिक संदर्भ हो सकते हैं, और इसलिए regexp को संशोधित करके गैर-कैप्चरिंग में परिवर्तित करना परिणाम को संशोधित कर सकता है (जब तक कि आप नहीं इसके अलावा आंतरिक संदर्भों के लिए जाँच करें, लेकिन समस्या तेजी से जटिल हो जाती है)। मुझे यह उत्सुक लगता है = ~ स्ट्रिंग.मैच की तुलना में तेज़ होगा।
djconnel

यहाँ regexp को फ्रीज़ करने से क्या लाभ है?
हार्दिक

जवाबों:


103

रूबी 2.4.0 के साथ शुरू, आप उपयोग कर सकते हैं RegExp#match?:

pattern.match?(string)

Regexp#match?स्पष्ट रूप में एक प्रदर्शन को बढ़ाने के रूप में सूचीबद्ध है 2.4.0 के लिए रिलीज नोट्स , के रूप में यह इस तरह के रूप में अन्य तरीकों द्वारा किया जाता वस्तु आवंटन से बचा जाता है Regexp#matchऔर =~:

Regexp # मैच?
जोड़ा गया Regexp#match?, जो बैक रेफरेंस ऑब्जेक्ट बनाने और $~ऑब्जेक्ट एलोकेशन को कम करने के लिए बदले बिना रेगेक्सपी मैच को अंजाम देता है ।


5
सुझाव के लिए धन्यवाद। मैंने बेंचमार्क स्क्रिप्ट को अपडेट किया है और Regexp#match?वास्तव में अन्य विकल्पों की तुलना में कम से कम 50% तेज है।
जियोले

74

यह एक सरल बेंचमार्क है:

require 'benchmark'

"test123" =~ /1/
=> 4
Benchmark.measure{ 1000000.times { "test123" =~ /1/ } }
=>   0.610000   0.000000   0.610000 (  0.578133)

"test123"[/1/]
=> "1"
Benchmark.measure{ 1000000.times { "test123"[/1/] } }
=>   0.718000   0.000000   0.718000 (  0.750010)

irb(main):019:0> "test123".match(/1/)
=> #<MatchData "1">
Benchmark.measure{ 1000000.times { "test123".match(/1/) } }
=>   1.703000   0.000000   1.703000 (  1.578146)

तो =~तेज है, लेकिन यह निर्भर करता है कि आप एक लौटे मूल्य के रूप में क्या चाहते हैं। यदि आप सिर्फ यह जांचना चाहते हैं कि पाठ में रेगेक्स है या नहीं=~


2
जैसा कि मैंने लिखा था, मैं पहले से ही पता चला है कि =~तेजी से है match, एक कम नाटकीय प्रदर्शन वृद्धि जब बड़ा regexps पर काम के साथ। मैं सोच रहा था कि क्या इस जांच को और भी तेज करने का कोई अजीब तरीका है, शायद रेगेक्सप या कुछ अजीब निर्माण में कुछ अजीब तरीके का शोषण करना।
gioele

मुझे लगता है कि
डगई

किस बारे में !("test123" !~ /1/)?
ma11hew28

1
@MattDiPasquale, दो बार उलटा तेजी से नहीं होना चाहिए"test123" =~ /1/
डगई

1
/1/.match?("test123")तेजी से है "test123" =~ /1/, तो यह केवल है अगर पाठ एक regex या शामिल नहीं की जाँच करने के।
नोरज

41

यह वह बेंचमार्क है जिसे मैंने नेट के चारों ओर कुछ लेख खोजने के बाद चलाया है।

2.4.0 के साथ विजेता re.match?(str)(जैसा कि @ wiktor-stribi )ew द्वारा सुझाया गया है), पिछले संस्करणों पर, re =~ strसबसे तेज़ प्रतीत होता है, हालाँकि str =~ reयह लगभग उतना ही तेज़ है।

#!/usr/bin/env ruby
require 'benchmark'

str = "aacaabc"
re = Regexp.new('a+b').freeze

N = 4_000_000

Benchmark.bm do |b|
    b.report("str.match re\t") { N.times { str.match re } }
    b.report("str =~ re\t")    { N.times { str =~ re } }
    b.report("str[re]  \t")    { N.times { str[re] } }
    b.report("re =~ str\t")    { N.times { re =~ str } }
    b.report("re.match str\t") { N.times { re.match str } }
    if re.respond_to?(:match?)
        b.report("re.match? str\t") { N.times { re.match? str } }
    end
end

MRI 1.9.3-o551 परिणाम:

$ ./bench-re.rb  | sort -t $'\t' -k 2
       user     system      total        real
re =~ str         2.390000   0.000000   2.390000 (  2.397331)
str =~ re         2.450000   0.000000   2.450000 (  2.446893)
str[re]           2.940000   0.010000   2.950000 (  2.941666)
re.match str      3.620000   0.000000   3.620000 (  3.619922)
str.match re      4.180000   0.000000   4.180000 (  4.180083)

परिणाम एमआरआई 2.1.5:

$ ./bench-re.rb  | sort -t $'\t' -k 2
       user     system      total        real
re =~ str         1.150000   0.000000   1.150000 (  1.144880)
str =~ re         1.160000   0.000000   1.160000 (  1.150691)
str[re]           1.330000   0.000000   1.330000 (  1.337064)
re.match str      2.250000   0.000000   2.250000 (  2.255142)
str.match re      2.270000   0.000000   2.270000 (  2.270948)

परिणाम MRI 2.3.3 (regex मिलान में एक प्रतिगमन है, ऐसा लगता है):

$ ./bench-re.rb  | sort -t $'\t' -k 2
       user     system      total        real
re =~ str         3.540000   0.000000   3.540000 (  3.535881)
str =~ re         3.560000   0.000000   3.560000 (  3.560657)
str[re]           4.300000   0.000000   4.300000 (  4.299403)
re.match str      5.210000   0.010000   5.220000 (  5.213041)
str.match re      6.000000   0.000000   6.000000 (  6.000465)

परिणाम एमआरआई 2.4.0:

$ ./bench-re.rb  | sort -t $'\t' -k 2
       user     system      total        real
re.match? str     0.690000   0.010000   0.700000 (  0.682934)
re =~ str         1.040000   0.000000   1.040000 (  1.035863)
str =~ re         1.040000   0.000000   1.040000 (  1.042963)
str[re]           1.340000   0.000000   1.340000 (  1.339704)
re.match str      2.040000   0.000000   2.040000 (  2.046464)
str.match re      2.180000   0.000000   2.180000 (  2.174691)

केवल नोट जोड़ने के लिए, शाब्दिक रूप इन की तुलना में तेज़ हैं। जैसे /a+b/ =~ strऔर str =~ /a+b/। यह फ़ंक्शंस के माध्यम से पुनरावृत्ति करने पर भी मान्य है और मैं इसे एक वैरिएबल पर नियमित अभिव्यक्तियों के भंडारण और ठंड से बेहतर मानने के लिए पर्याप्त वैध देखता हूं। मैंने रूबी 1.9.3p547, रूबी 2.0.0p481 और रूबी 2.1.4p265 के साथ अपनी स्क्रिप्ट का परीक्षण किया। यह संभव है कि ये सुधार बाद के पैच पर किए गए थे, लेकिन मेरे पास इसके पहले संस्करणों / पैच के साथ परीक्षण करने की कोई योजना नहीं है।
konsolebox

मैंने सोचा !(re !~ str)कि यह तेज़ हो सकता है, लेकिन ऐसा नहीं है।
ma11hew28

7

क्या re === str(मामले की तुलना) के बारे में ?

चूंकि यह सही या गलत का मूल्यांकन करता है और मैचों के भंडारण, मैच इंडेक्स और उस सामान को वापस लाने की कोई आवश्यकता नहीं है, मुझे आश्चर्य है कि अगर यह मिलान करने का एक और भी तेज़ तरीका होगा =~


ठीक है, मैंने यह परीक्षण किया। =~भले ही आपके पास कई कैप्चर समूह हों, लेकिन यह अन्य विकल्पों की तुलना में अधिक तेज़ है।

BTW, क्या अच्छा है freeze? मैं इससे किसी भी प्रदर्शन को बढ़ावा नहीं दे सकता।


के प्रभाव freezeइच्छा परिणामों में दिखाई नहीं है क्योंकि यह बेंचमार्क छोरों से पहले होता है, और पैटर्न पर ही कार्य करता है।
टिन मैन

4

आपकी नियमित अभिव्यक्ति कितनी जटिल है, इसके आधार पर, आप संभवतः केवल साधारण स्ट्रिंग स्लाइसिंग का उपयोग कर सकते हैं। मुझे आपके आवेदन के लिए इसकी व्यावहारिकता के बारे में निश्चित नहीं है या यह वास्तव में कोई गति सुधार प्रदान करेगा या नहीं।

'testsentence'['stsen']
=> 'stsen' # evaluates to true
'testsentence'['koala']
=> nil # evaluates to false

मैं स्ट्रिंग स्लाइसिंग का उपयोग नहीं कर सकता क्योंकि regexp रन-टाइम पर प्रदान किया जाता है और मेरा उस पर कोई नियंत्रण नहीं है।
gioele

आप स्ट्रिंग स्लाइसिंग का उपयोग कर सकते हैं, बस निश्चित-स्ट्रिंग का उपयोग करके स्लाइसिंग नहीं कर सकते। उद्धरण में एक स्ट्रिंग के बजाय एक चर का उपयोग करें और यह अभी भी काम करेगा।
टिन मैन

3

मैं सोच रहा था कि क्या इस जांच को और भी तेज करने का कोई अजीब तरीका है, शायद रेगेक्सप या कुछ अजीब निर्माण में कुछ अजीब तरीके का शोषण करना।

रेगेक्सपी इंजन अलग-अलग हैं कि वे खोजों को कैसे लागू करते हैं, लेकिन सामान्य तौर पर, गति के लिए अपने पैटर्न को लंगर डालते हैं, और लालची मैचों से बचते हैं, खासकर जब लंबे तारों की खोज करते हैं।

सबसे अच्छी बात, जब तक आप परिचित न हों कि कोई विशेष इंजन कैसे काम करता है, बेंचमार्क करना और एंकर को जोड़ना, खोजों को सीमित करना, वाइल्डकार्ड बनाम स्पष्ट मैचों का उपयोग करना, आदि।

फल मणि, जल्दी से बेंचमार्किंग बातों के लिए बहुत उपयोगी है क्योंकि यह स्मार्ट है। रूबी की बनी बेंचमार्क कोड भी उपयोगी है, हालांकि आप ऐसे परीक्षण लिख सकते हैं जो आपको सावधान न करके मूर्ख बनाते हैं।

मैंने स्टैक ओवरफ्लो पर यहां कई उत्तरों में दोनों का उपयोग किया है, इसलिए आप मेरे उत्तरों के माध्यम से खोज कर सकते हैं और आपको बहुत सारे ट्रिक और परिणाम दिखाई देंगे कि कैसे आप तेजी से कोड लिखने के विचार दे सकते हैं।

याद रखने की सबसे बड़ी बात यह है कि समय से पहले अपने कोड को ऑप्टिमाइज़ करना बुरा है, इससे पहले कि आपको पता चले कि मंदी कहाँ है।


0

Wiktor Stribiżew और डगुई के उत्तर को पूरा करने के लिए मैं कहूंगा कि /regex/.match?("string")जितना जल्दी हो सके"string".match?(/regex/)

रूबी 2.4.0 (10 000 000 ~ 2 सेकंड)

2.4.0 > require 'benchmark'
 => true 
2.4.0 > Benchmark.measure{ 10000000.times { /^CVE-[0-9]{4}-[0-9]{4,}$/.match?("CVE-2018-1589") } }
 => #<Benchmark::Tms:0x005563da1b1c80 @label="", @real=2.2060338060000504, @cstime=0.0, @cutime=0.0, @stime=0.04000000000000001, @utime=2.17, @total=2.21> 
2.4.0 > Benchmark.measure{ 10000000.times { "CVE-2018-1589".match?(/^CVE-[0-9]{4}-[0-9]{4,}$/) } }
 => #<Benchmark::Tms:0x005563da139eb0 @label="", @real=2.260814556000696, @cstime=0.0, @cutime=0.0, @stime=0.010000000000000009, @utime=2.2500000000000004, @total=2.2600000000000007> 

रूबी 2.6.2 (100 000 000 ~ 20 सेकंड)

irb(main):001:0> require 'benchmark'
=> true
irb(main):005:0> Benchmark.measure{ 100000000.times { /^CVE-[0-9]{4}-[0-9]{4,}$/.match?("CVE-2018-1589") } }
=> #<Benchmark::Tms:0x0000562bc83e3768 @label="", @real=24.60139879199778, @cstime=0.0, @cutime=0.0, @stime=0.010000999999999996, @utime=24.565644999999996, @total=24.575645999999995>
irb(main):004:0> Benchmark.measure{ 100000000.times { "CVE-2018-1589".match?(/^CVE-[0-9]{4}-[0-9]{4,}$/) } }
=> #<Benchmark::Tms:0x0000562bc846aee8 @label="", @real=24.634255946999474, @cstime=0.0, @cutime=0.0, @stime=0.010046, @utime=24.598276, @total=24.608321999999998>

नोट: समय भिन्न होता है, कभी-कभी /regex/.match?("string")तेज होता है और कभी-कभी "string".match?(/regex/), अंतर केवल मशीन गतिविधि के कारण होता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.