theo hat geschrieben: Do 5. Jan 2023, 18:00
Wenn ich das richtig mitbekommen habe, hast du auch als Experte nicht auf Anhieb verstanden, was dieser Ausdruck tut.
Code: Alles auswählen
(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}
Nur darum geht es mir.
Wenn man regex im vakuum betrachtet dann ist es natürlich etwas aufwendiger rauszubekommen, aber nehmen wir mal ne realistische situation an, man findet diese Zeile:
Code: Alles auswählen
IPv4Regexpr := TRegExpr.Create( '(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}');
Und ich kann dir auf anhieb sagen das das ne IPv4 addresse matchen soll. Wenn man mit dem wissen sich das anschaut macht es auch direkt Sinn.
m.fuchs hat geschrieben: Do 5. Jan 2023, 21:30
Ich sage daher: Vergesst erst einmal die Effizienz des Quellcodes, was richtig Arbeitszeit (und damit Geld) kostet ist nicht-lesbarer Quellcode. In den meisten Fällen wird sich auch die Frage nach 50 Millisekunden mehr gar nicht stellen, einfach weil der Benutzer dies niemals bemerkt. Bei der
Originalfragestellung im Ursprungs-Thread wird das wohl auch so sein - Einlesen und Bearbeiten von Config-Dateien ist selten etwas zeitkritisches.
Da stimme ich komplett zu, wenn es um simple optimierungen geht, aber algorithmische Effizienz ist ein ganz anderes Biest.
Der unterschied zwischen O(n) und O(n^2) macht vielleicht bei den tests mit ein paar kb nur wenige millisekunden, und dann schiebt der nutzer eine datei von einem MB rein und plötzlich dauerts beim Nutzer jahre. Bei diesem unterschied bedeutet wenn man den Input um einen Faktor 10 größer macht, dauert es 100x länger. Bzw bei dem Beispiel von KB auf MB dauerts halt dann einen Faktor 1000000 länger.
Das sind nicht mal eben 50 millisekunden, das ist der unterschied von 30ms auf über 1 jahr.
Vor allem bedeutet es auch das selbst wenn ich mir einen 10 mal schnelleren rechner hole ich trozdem nur 3x mehr input in der gleichen zeit geparsed bekomme.
Das beste beispiel hier ist Sortieren, Bubble sort sind 5 zeilen oder so extrem einfach zu bauen und zu lesen. Im vergleich dazu sind Quicksort oder heapsort relativ kompliziert. Aber trozdem baut absolut niemand in produktionssoftware Bubblesort ein, genau deshalb, man hat hier nicht das problem von ein paar MS, sondern der Geschwindigkeitsverlust geht relativ schnell in den minuten bis stunden bereich für sachen die sonst in wenigen Sekunden fertig sind.
kupferstecher hat geschrieben: Do 5. Jan 2023, 18:02
Der Regexautomat muss doch auch das Pattern auf Byteebene durchprobieren. Der Faktor m (steht wohl für die Länge des Pattern) ist dann halt in O versteckt.
Tatsächlich ist die antwort hier ein klares Jein. Es gibt den unterschied zwischen dem Matchen und dem Finden, beim Matchen (die Frage ob ein string in gänze eine bestimmte form hat) braucht man kein backtracking, da ist die Laufzeit O(n). Beim suchen wo das pattern irgendwo auftauchen kann muss man backtracken und braucht O(n*m) wobei m der maximale substring vom input ist der auf das pattern matchen kann (bei fixen patterns konstant, bei dynamischen ist m potentiell = n und damit ist es O(n^2)).
Allerdings kann man einen Seaching ausdruck in einen Matching ausdruck verwandeln indem man .* davor hängt.
Also der string "Hallo Welt" matcht nicht "Welt" weil noch ein Hallo davor ist aber er matcht ein ".*Welt". Allerdings erlaubt das nicht das mehrfache finden des selben ausdrucks in einem string also "Hallo Welt Welt" würde immer nur das erste "Hallo Welt" matchen, aber das zweite Welt würde man nicht finden können. Dafür wird dann Searching mit Backtracking benötigt.
Im endeffekt kommt das aber auch auf die Implementation an. Ich glaube TRegExpr kann gar nicht Matchen sondern immer Searchen, und durch die Teuren String Operationen ist ein Prefixen mit .* deutlich ineffizienter statt zu searchen. In C++ aber gibt es genau dafür den unterschied zwischen std::regex_match und std::regex_search, genau wegen dieser performance unterschiede.