== oder String.Equals



  • Stimmt, if(foundMn == true) sieht wirklich etwas hässlich aus. Geschwindigkeitsmässig macht es aber keinen Unterschied ob das "== true" da ist oder nicht.
    Was mich allerdings wundert ist, dass die Methode mehr Zeit benötigt wenn ich anstatt

    if(XmlText.Substring(i + 1, mnLength + 2).Equals('/' + masterNode + '>'))
    

    am Anbfang eine String Variable "mnClosing = '/' + masterNode + '>'" definiere und diese dann in der if-Abfrage verwende:

    if(XmlText.Substring(i + 1, mnLength + 2).Equals(mnClosing))    // das ist langsamer als Concat()
    

    Normalerweise sollte doch die zweite Ausführung die bessere sein, da der GC nicht bei jedem Durchgang einen neuen String zusammensetzen muss.



  • tommazzo schrieb:

    Das komische hierbei ist, dass die Methode, in der ich den Xml String parse und in der alle Vergleiche stattfinden mit String.Equals() langsamer läuft als mit dem Operator ==.

    MSDN schrieb:

    This operator is implemented using the Equals method, ...

    Link

    Insofern wirds irgendwie schwer, dass Equals wirklich langsamer sein sollte 🙄



  • Das ist sehr komisch. Laut versch. Internetseiten (hier ist mal eine soll String.Equals() schneller sein als der == Operator. Laut dieser Seite ruft der == Operator die Methode String.op_Equality() auf. Wenn man eine Assembly mit ildasm auseinandernimmt kann man das auch sehen:

    IL_00d8:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                     string)
    

    Warum diese Methode letztendlich langsamer ist als String.Equals() verstehe ich nicht.



  • Das ist der Operator in IL Code.

    .method public hidebysig specialname static 
            bool  op_Equality(string a,
                              string b) cil managed
    {
      // Codegröße       8 (0x8)
      .maxstack  8
      IL_0000:  ldarg.0
      IL_0001:  ldarg.1
      IL_0002:  call       bool System.String::Equals(string,
                                                      string)
      IL_0007:  ret
    } // end of method String::op_Equality
    


  • Anscheinend ist String.Equals() doch schneller als op_Equality()!
    Eine Misinterpretation einer Profileraussage hat dazu geführt das es anfangs nicht so aussah, aber nach zahlreichen Tests bin ich mir sicher: String.Equals() ist schneller. 🙂
    Danke für eure Hilfe!



  • Dieser Operator ist unter Verwendung der Equals-Methode implementiert. Dies bedeutet, dass die Operanden auf eine Kombination von Verweis- und Wertgleichheit überprüft werden. Beim Vergleich wird die Groß- und Kleinschreibung berücksichtigt.



  • Jetzt macht er seinen ganzen Code hässlich, weil er sich manchmal das eine einbildet, manchmal das andere... obwohl beides das selbe macht. 🙄
    Junge, optimiere woanders.



  • Optimizer schrieb:

    Jetzt macht er seinen ganzen Code hässlich, weil er sich manchmal das eine einbildet, manchmal das andere... obwohl beides das selbe macht. 🙄
    Junge, optimiere woanders.

    Anscheinend macht beides zwar das selbe, eines macht es halt nur langsamer als das andere. Nach mehreren Geschwindigkeitstests habe ich nach den Optimierungen, die aber noch etwas weitläufiger waren als nur == mit Equals() zu ersetzen, eine Geschwindigkeitserhöhung von 20-25% feststellen können. Wenn du aber irgendwelche Vorschläge hast, wo ich noch etwas optimieren könnte, dann nur heraus damit.



  • tommazzo schrieb:

    Anscheinend macht beides zwar das selbe, eines macht es halt nur langsamer als das andere. Nach mehreren Geschwindigkeitstests habe ich nach den Optimierungen, die aber noch etwas weitläufiger waren als nur == mit Equals() zu ersetzen, eine Geschwindigkeitserhöhung von 20-25% feststellen können. Wenn du aber irgendwelche Vorschläge hast, wo ich noch etwas optimieren könnte, dann nur heraus damit.

    Leb ruhig weiter in deiner naiven Traumwelt, in der deine wahrscheinlich sehr fragwürdigen Messungen ergeben,
    dass absolut das gleich unterschiedlich schnell sein soll. Solche Ergebniss hängen von viel zu vielen Kriterien ab,
    als du generell sagen könntest dieses oder jenes ist schneller.

    Versuch lieber mal vernünftig strukturierten Code zu schreiben, bevor du zu optimieren beginnst.
    Ich würde an deiner Stelle zum Beispiel mal eher versuchen ein wenig Regex zu verwenden ...



  • ... schrieb:

    Versuch lieber mal vernünftig strukturierten Code zu schreiben, bevor du zu optimieren beginnst.

    1. Was ist an meinem Code so unstrukturiert?
    und
    2. Mir ist schon klar, dass es bei der Geschwindigkeit auf verschiedene Faktoren ankommt. Ich spreche hier auch nur über eine Testserie bei der ich bestimmte Knoten hintereinander ausgelesen habe - und bei der gab es eben eine kleine Geschwindigkeitszunhame. Ich weiss auch, dass diese kleine Zunahme bei den meisten Operationen egal ist, wenn aber viele Operationen hintereinander ausgeführt werden, kann sie schon ins Gewicht fallen.

    P.S.: Meine Hauptoptimierung in dem Codestück, das ich gepostet habe läuft mittlerweile (und darauf beziehen sich meine letzten Testergebnisse) nicht nur auf String.Equals(), sondern eher auf GC Optimierungen hinaus:

    string mnEnd = masterNode + '>';
    for(i = 0; i < XmlText.Length; i++) {
    				if(XmlText[i] == '<') {			
    
    					if(!foundMn) {	
    						if(mnLength + 1 <= XmlText.Length - i - 1) {			
    							if(XmlText.Substring(i + 1, mnLength + 1).Equals(mnEnd))    /* anstatt den String mnEnd = masterNode + '>' bei jedem Durchlauf neu
    zusammenzusetzen wird der String nur einmal am Anfang zusammengesetzt */
    


  • tommazzo schrieb:

    ... schrieb:

    Versuch lieber mal vernünftig strukturierten Code zu schreiben, bevor du zu optimieren beginnst.

    1. Was ist an meinem Code so unstrukturiert?

    Soll das ein schlechter Witz sein? Ich sehe hier eine fette Funktion, 200 Zeilen lang, 200 Spalten breit. Geschachtelte ifs und Schleifen ohne Ende.
    Du hast dir die Chance auf Optimierung schon total verbaut. Kein Schwein kann deinen Code noch optimieren. Dazu müsste er sich erstmal durch diesen Wulst quälen, ihn verstehen und den Überblick bewahren, um noch Schwachstellen in deinem Algorithmus zu finden. Das sieht man ja schon an deinen Kommentaren. Nach jeder Zeile steht ein Kommentar, anstatt dass die Zeile für sich selbst spricht. Aber das braucht man hier auch so, sonst behält man den Überblick nicht mehr.

    Selbst wenn Equals() schneller wäre, was es nicht ist, wäre der Geschwindigkeitszuwachs ein Witz im Vergleich zu dem, was man am Algorithmus vielleicht drehen könnte. Aber dieser fette, hässliche Code-Batzen, jetzt auch noch mit dem Equals(), ist so unübersichtlich, dass man hier keine Verbesserungen mehr finden kann.
    Optimierung fängt damit an, dass du den Code leserlich machst (oder von Anfang an so schreibst). Dass du viele kleine Funktionen hast, jede nur 10 Zeilen lang und 80 Spalten breit. Jede macht nur eine kleine Teilaufgabe vom Ganzen. Und auf einmal siehst du, dass du unnötigerweise diesen Teilschritt viel zu oft machst. Du könntest eine Schleife viel früher abbrechen. Oder den Wert merken. Oder einen völlig anderen Algorithmus finden. Das geht aber nur, wenn man die Übersicht hat. Vielleicht glaubst du, den Überblick zu haben, aber um was zu verbessern, braucht man nochmal nen kleinen Bonus. Das hilft ja auch beim denken. Du kannst nur den einen kleinen Teilschritt anschauen und überlegen, ob er wirklich nötig ist.
    Und du erhältst ein viel detaillierteres Profil vom Profiler.



  • Diese Riesenmethode auf mehrere kleine aufzuteilen ist schon eine gute Idee, nur frage ich mich ob es in diesem Fall viel bringt ohne gleich den ganzen Algorithmus (der ja eigentlich problemlos funktioniert) neu zuschreiben.



  • tommazzo schrieb:

    Diese Riesenmethode auf mehrere kleine aufzuteilen ist schon eine gute Idee, nur frage ich mich ob es in diesem Fall viel bringt ohne gleich den ganzen Algorithmus (der ja eigentlich problemlos funktioniert) neu zuschreiben.

    Ich hab zwar deine Funktion kaum angesehen aber bei mir klingelt da schon ein Wort: "parsen" in deinem ersten Beitrag. Das schreit nach einer ganzen Klasse und nicht nur nach einer Funktion. XML kann man schön rekursiv bearbeiten, sich ne kleine Gramatik zusammenstellen und die dann abfahren usw....



  • Online schrieb:

    Ich hab zwar deine Funktion kaum angesehen aber bei mir klingelt da schon ein Wort: "parsen" in deinem ersten Beitrag. Das schreit nach einer ganzen Klasse und nicht nur nach einer Funktion. XML kann man schön rekursiv bearbeiten, sich ne kleine Gramatik zusammenstellen und die dann abfahren usw....

    Eigentlich ist die Funktion Teil einer ganzen Reader-Klasse.

    Ich habe jetzt aber beschlossen den ganzen Algorithmus in den Müll zu werfen und ihn von Anfang an neu zuschreiben. Dazu habe ich das Knuth-Morris-Pratt Verfahren ins Auge gefasst. Damit sollte das Auffinden eines Knotens schneller laufen. 🙂



  • tommazzo schrieb:

    [...]
    Eigentlich ist die Funktion Teil einer ganzen Reader-Klasse.
    [...]

    Nichtdesto trotz kann man da noch eine Klasse rumbauen 😉



  • Hier ist meine neue statische Reader Klasse, in der es allerdings noch ein Problem gibt: Wenn ich einen Knoten auslese, bekomme ich oft nicht nur den Inhalt sondern, nach dem Anfang des Knotens den Rest der gesamten Datei zurückgeliefert.

    Vielleicht fällt jemandem etwas dazu ein, hier ist der Code:

    internal class NodeHandler
    	{
    		#region Knuth-Morris-Pratt algorithm
    
    		/// <summary>
    		/// generates a KMP search table for the specified search-string
    		/// </summary>
    		/// <param name="searchStr">the string for which the table should be generated</param>
    		private static int[] GetKMP_Table(ref string searchStr) {
    			int i, j;
    			int[] next = new int[searchStr.Length + 1];
    
    			next[i = 0] = j = -1;
    
    			do {
    				if(j < 0 || searchStr[i] == searchStr[j])
    					next[++i] = ++j;
    				else
    					j = next[j];
    			} while(i < searchStr.Length - 1);
    
    			return next;
    		}
    
    		/// <summary>
    		/// uses the Knuth-Morris-Pratt algorithm to find the next occurance of the search string
    		/// </summary>
    		/// <param name="xmlText">our xml text</param>
    		/// <param name="searchStr">the string we're looking for</param>
    		/// <param name="next">the KMP jump table for the search string</param>
    		/// <param name="startPos">the starting position</param>
    		/// <param name="endPos">the max search position</param>
    		/// <returns>the index of the first char of the found search string or -1 if the search string has not been found in xmlText</returns>
    		private static int GetNextPos(ref string xmlText, ref string searchStr, int[] next, int startPos, int endPos) {
    			int textLength = endPos - startPos + 1;
    			int j = 0;
    
    			// Knuth-Morris-Pratt algorithm
    			for(int i = startPos; i < textLength; i++) {
    
    				// endless loop executing as long as chars match
    				for(;;) {
    					if(xmlText[i] == searchStr[j]) {
    						j++;
    						if(j == searchStr.Length)
    							return i - searchStr.Length + 1;
    						break;
    					}
    					else {
    						if(j == 0)
    							break;
    						else
    							j = next[j];
    					}
    				}
    			}
    
    			return -1;
    		}
    
    		#endregion
    
    		#region FindNode
    
    		/// <summary>
    		/// finds the specified node in xmlText
    		/// </summary>
    		/// <param name="nBegin">out: the beginning of the found node (will be -1 if node was not found)</param>
    		/// <param name="nEnd">out: the end of the found node (will be endPos if node was not found)</param>
    		/// <param name="startPos">the starting position</param>
    		/// <param name="endPos">the max search position</param>
    		/// <param name="includeNodes">specified whether the nodes themselved will be included in nBeginning and nEnd</param>
    		private static void FindNode(out int nBegin, out int nEnd, ref string xmlText, ref string node, int index, int startPos, int endPos, bool includeNodes) {
    			string nodeBegin = '<' + node + '>';
    			int[] next;
    			int foundCount = 0;
    			nBegin = 0;
    			nEnd = 0;
    
    			// let's find nodeBegin
    			next = GetKMP_Table(ref nodeBegin);
    			while(foundCount < index) {
    				nBegin = GetNextPos(ref xmlText, ref nodeBegin, next, nBegin, endPos);
    				if(nBegin != -1)
    					foundCount++;
    				else {
    					nEnd = endPos;
    					return;
    				}
    				nBegin += nodeBegin.Length;
    			}
    
    			// let's find nodeEnd
    			string nodeEnd = "</" + node + '>';
    			next = GetKMP_Table(ref nodeEnd);
    
    			nEnd = GetNextPos(ref xmlText, ref nodeEnd, next, nBegin, endPos);
    			if(nEnd == -1) {
    				nEnd = endPos;
    				return;
    			}
    
    			// check if the nodes themselves have to be included in the results and add them if necessarry
    			if(includeNodes) {
    				nBegin -= nodeBegin.Length;
    				nEnd += nodeEnd.Length;
    			}
    		}
    
    		#endregion
    
    		internal static TXmlReader.ReaderReturnValue GetNodeContent(ref string xmlText, ref string masterNode, int mnIndex, ref string subNode, int snIndex) {
    			int mnBegin, mnEnd, snBegin, snEnd;
    			TXmlReader.ReaderReturnValue returnValue = new TXML.TXmlReader.ReaderReturnValue();
    
    			// let's find our masterNode
    			if(masterNode.Length > 0) {
    				FindNode(out mnBegin, out mnEnd, ref xmlText, ref masterNode, mnIndex, 0, xmlText.Length, false);
    				if(mnBegin == -1) {
    					returnValue.foundNode = false;
    					return returnValue;
    				}
    			}
    			else {
    				mnBegin = 0;
    				mnEnd = xmlText.Length;
    			}
    
    			// let's find our subNode
    			FindNode(out snBegin, out snEnd, ref xmlText, ref subNode, snIndex, mnBegin, mnEnd, false);
    			if(snBegin == -1) {
    				returnValue.foundNode = false;
    				return returnValue;
    			}
    
    			// get and return the substring
    			returnValue.foundNode = true;
    			returnValue.text = xmlText.Substring(snBegin, snEnd - snBegin);
    			return returnValue;
    		}
    	}
    

    P.S.: Um den Inhalt eines Knotens auszulesen wird die Methode TXmlReader.ReaderReturnValue GetNodeContent() (ganz unten) aufgerufen.
    ReaderReturnValue ist eine Klasse mit zwei Membern:
    bool foundNode gibt an ob der Knoten gefunden wurde
    string text gibt den Inhalt des gefundenen Knotens wieder



  • Hallo Meister...

    es bringt nichts, einen Referenztypen wie string per ref zu übergeben. by value wird die Referenz von 4 Byte kopiert byref wird die adresse von 4 byte kopiert und du zahlst zusätzlich für die Zugriffe, wenn du Pech hast. Du solltest langsam mit diesen eingebildeten Optimierungen (dazu gehört auch Equals()) aufhören und wirklich konkret über den Algorithmus nachdenken. Online hat dir ja auch schon Ansatzpunkte genannt.
    Hör bitte auf, deinen Code hässlich zu machen. Du behinderst dich nur selbst.



  • Wenn du mal einen Blick auf den obigen Code wirfst, dann wirst du sehen, dass das eine ganz neue Klasse ist, die kein Bisschen des alten Codes enthält. Ich habe sehr wohl über den Algorithmus nachgedacht und einen völlig neuen gefunden: ich verwende jetzt einen Algorithmus der Marke "Knuth-Morris-Pratt". Irgendwie stimmt aber etwas noch nicht in dem Code. Ich kann meistens nicht den Endteil eines Knotens (</meinKnoten>) finden.

    P.S.: Strings werden bei Methoden aber ohne das ref Keyword als Value-Types übergeben, obwohl sie eigentlich Reference-Types sind.



  • tommazzo schrieb:

    P.S.: Strings werden bei Methoden aber ohne das ref Keyword als Value-Types übergeben, obwohl sie eigentlich Reference-Types sind.

    Achja? Wo hast du denn das her? Würd mich ja mal interessieren.
    Den String zu übergeben, heißt bereits, die Referenz zu übergeben. Du gewinnst nichts, wenn du eine Referenz auf die Referenz übergibst. Das ist völlig sinnlos.



  • Schreib dir mal ein kleines Testprogramm: in Main() deklarierst du einen String, den du mit irgendeinem Text initialisierst. Dann übergibst du den String an eine zweite Methode, die ihn modifiziert. Wenn du den String nachher in Main() nochmal am Bildschirm ausgibst, wirst du sehen, dass dieser String den selben Wert hat, mit dem du ihn initialisiert hast.


Anmelden zum Antworten