Was ist der schnellste Weg, um zu überprüfen, ob eine Zeichenfolge mit einem regulären Ausdruck in Ruby übereinstimmt?
Mein Problem ist, dass ich eine große Liste von Zeichenfolgen "egrep" durchgehen muss, um herauszufinden, welche mit einem regulären Ausdruck übereinstimmen, der zur Laufzeit angegeben wird. Es ist mir nur wichtig, ob die Zeichenfolge mit dem regulären Ausdruck übereinstimmt, nicht, wo sie übereinstimmt oder wie der Inhalt der übereinstimmenden Gruppen ist. Ich hoffe, dass diese Annahme verwendet werden kann, um die Zeit zu reduzieren, die mein Code mit passenden regulären Ausdrücken verbringt.
Ich lade den regulären Ausdruck mit
pattern = Regexp.new(ptx).freeze
Ich habe festgestellt, dass string =~ pattern
das etwas schneller ist als string.match(pattern)
.
Gibt es andere Tricks oder Verknüpfungen, mit denen dieser Test noch schneller durchgeführt werden kann?
quelle
Antworten:
Ab Ruby 2.4.0 können Sie Folgendes verwenden
RegExp#match?
:Regexp#match?
wird in den Versionshinweisen für 2.4.0 explizit als Leistungsverbesserung aufgeführt , da Objektzuweisungen vermieden werden, die von anderen Methoden wieRegexp#match
und ausgeführt werden=~
.quelle
Regexp#match?
ist in der Tat mindestens 50% schneller als die anderen Alternativen.Dies ist ein einfacher Maßstab:
Ist
=~
also schneller, hängt aber davon ab, was Sie als Rückgabewert haben möchten. Wenn Sie nur überprüfen möchten, ob der Text einen regulären Ausdruck enthält oder nicht=~
quelle
=~
schneller ist alsmatch
bei einer weniger dramatischen Leistungssteigerung, wenn mit größeren regulären Ausdrücken gearbeitet wird. Was ich mich frage, ist, ob es eine seltsame Möglichkeit gibt, diese Überprüfung noch schneller zu machen, vielleicht eine seltsame Methode in Regexp oder ein seltsames Konstrukt auszunutzen.!("test123" !~ /1/)
?"test123" =~ /1/
/1/.match?("test123")
ist schneller als"test123" =~ /1/
wenn nur geprüft werden soll, ob der Text einen regulären Ausdruck enthält oder nicht.Dies ist der Benchmark, den ich durchgeführt habe, nachdem ich einige Artikel im Internet gefunden habe.
Mit 2.4.0 scheint der Gewinner
re.match?(str)
(wie von @ wiktor-stribiżew vorgeschlagen) in früheren Versionenre =~ str
am schnellsten zu sein, obwohl erstr =~ re
fast genauso schnell ist.Ergebnisse MRT 1.9.3-o551:
Ergebnisse MRT 2.1.5:
Ergebnisse MRT 2.3.3 (es scheint eine Regression beim Regex-Matching zu geben):
Ergebnisse MRT 2.4.0:
quelle
/a+b/ =~ str
undstr =~ /a+b/
. Es ist auch dann gültig, wenn sie durch Funktionen iteriert werden, und ich sehe dies als ausreichend an, um es als besser zu betrachten, als reguläre Ausdrücke in einer Variablen zu speichern und einzufrieren. Ich habe mein Skript mit Ruby 1.9.3p547, Ruby 2.0.0p481 und Ruby 2.1.4p265 getestet. Es ist möglich, dass diese Verbesserungen an späteren Patches vorgenommen wurden, aber ich habe noch nicht vor, sie mit früheren Versionen / Patches zu testen.!(re !~ str)
könnte schneller sein, aber es ist nicht so.Wie wäre es mit
re === str
(Fallvergleich)?Da es als wahr oder falsch ausgewertet wird und keine Übereinstimmungen, Rückgabe des Übereinstimmungsindex und dergleichen benötigt werden, frage ich mich, ob dies eine noch schnellere Methode zum Abgleichen wäre als
=~
.Ok, ich habe das getestet.
=~
ist immer noch schneller, selbst wenn Sie mehrere Erfassungsgruppen haben, ist jedoch schneller als die anderen Optionen.Übrigens, was nützt das
freeze
? Ich konnte keinen Leistungsschub daran messen.quelle
freeze
werden in den Ergebnissen nicht angezeigt, da sie vor den Benchmark-Schleifen auftreten und auf das Muster selbst einwirken.Abhängig davon, wie kompliziert Ihr regulärer Ausdruck ist, können Sie möglicherweise einfach das Schneiden von Zeichenfolgen verwenden. Ich bin mir nicht sicher, ob dies für Ihre Anwendung praktikabel ist oder ob es tatsächlich Geschwindigkeitsverbesserungen bietet.
quelle
Regexp-Engines unterscheiden sich in der Art und Weise, wie Suchvorgänge implementiert werden. Verankern Sie jedoch im Allgemeinen Ihre Muster auf Geschwindigkeit und vermeiden Sie gierige Übereinstimmungen, insbesondere bei der Suche nach langen Zeichenfolgen.
Das Beste, was Sie tun können, bis Sie mit der Funktionsweise einer bestimmten Engine vertraut sind, ist, Benchmarks durchzuführen und Anker hinzuzufügen / zu entfernen, die Suche einzuschränken, Platzhalter im Vergleich zu expliziten Übereinstimmungen zu verwenden usw.
Das fruchtige Juwel ist sehr nützlich, um Dinge schnell zu vergleichen, weil es intelligent ist. Rubys integrierter Benchmark Code ist ebenfalls nützlich, obwohl Sie Tests schreiben können, die Sie täuschen, wenn Sie nicht vorsichtig sind.
Ich habe beide in vielen Antworten hier auf Stack Overflow verwendet, sodass Sie meine Antworten durchsuchen können und viele kleine Tricks und Ergebnisse sehen können, um Ihnen Ideen zu geben, wie Sie schnelleren Code schreiben können.
Das Wichtigste ist, dass Sie Ihren Code nicht vorzeitig optimieren, bevor Sie wissen, wo die Verlangsamungen auftreten.
quelle
Um die Antworten von Wiktor Stribiżew und Dougui zu vervollständigen, würde ich das
/regex/.match?("string")
ungefähr so schnell sagen wie"string".match?(/regex/)
.Ruby 2.4.0 (10 000 000 ~ 2 Sek.)
Ruby 2.6.2 (100 000 000 ~ 20 Sek.)
Hinweis: Die Zeiten variieren, manchmal
/regex/.match?("string")
sind sie schneller und manchmal"string".match?(/regex/)
sind die Unterschiede möglicherweise nur auf die Maschinenaktivität zurückzuführen.quelle