== oder String.Equals



  • 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.



  • Bitte kauf Dir ein Buch oder lass die Hände vom Programmieren!

    private void button_Click(object sender, System.EventArgs e)
    {
    	string t = "Bar";
    	MessageBox.Show( t );  // Gibt Bar aus
    	t = Foo( t ) ;
    	MessageBox.Show( t );  // Gibt Foo aus
    }
    
    private string Foo( string s )
    {
    	return ( s = "Foo" );
    }
    


  • Ich denk mal es ist eigentlich klar was er meint ..

    using System;
    
    static class StringRefTest 
    {
        public static void ChangeString(string s) {
            s = "muahahahahahhaha !!!!!";
        }
    
        public static void Main() {
            string str = "original";
            Console.WriteLine(str); // "original"
    
            ChangeString(str);
            Console.WriteLine(str); // "original"
        }
    }
    

    Das liegt aber ganz bestimmt nicht daran, dass String als ValueType übergeben wird, denn das ist einfach nur an
    der Versuch das Verhalten durch "Raten" zu erklären ..

    Das liegt halt einfach daran, dass wir einfach eine Referenz (s) auf ein anderes Objekt (mit dem Inhalt "muahahahahahhaha !!!!!")
    zeigen lassen. Die "Original-Refernz" (str) ist dabei nicht beteiligt, da ihre Adresse kopiert wurde.

    tommazzo schrieb:

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

    Korrekter wäre es wohl zu sagen:
    Strings imiteren das Verhalten von Value-Types, da sie immutable sind. Sie sind aber dennoch in jedem Fall Reference-Types.

    Wobei aber auch, so denke ich halt, klar ist was Optimizer meinte. Sieh dir zum Beispiel mal deine Methode GetKMP_Table() an.
    Du übergibst hier den String vollkommen unnötigerweise per ref, denn du veränderst ihn nicht, wofür also das ref ?
    Optimizer wird wohl gedacht haben, du versprichst dir Performance Steigerung in punkto CallByValue vs. CallByReference.
    Würde zumindestens zu Antworten passen, wie etwa:

    Optimizer schrieb:

    Den String zu übergeben, heißt bereits, die Referenz zu übergeben.



  • Uups, mit dem Beispiel war ich etwas voreilig, naja Morgenstund ... 😃



  • OK, ich denke ich hab's jetzt begriffen 🙂 und werde die REFs entfernen.
    Das hilft mir aber immer noch nicht bei meinem Problem. Nehmen wir mal an ich möchte diese XML Datei auslesen:

    <?xml version="1.0" encoding="utf-8"?>
    <myRootElement>
        <myStringNode>foo</myStringNode>
        <mastverNode>
        	<myIntNode>2</myIntNode>
        </mastverNode>
        <myBoolNode>True</myBoolNode>
    </myRootElement>
    

    Wenn ich nun "myStringNode" auslesen will bekomme ich "foo" als Rückgabestring.
    Wenn ich allerdings "myBoolNode" auslesen will bekomme ich das zurück:

    True</myBoolNode>
    </myRootElement>
    

    Aus irgendeinem Grund findet das Ding oft nicht das Ende des Knotens.



  • Hier ist noch einmal der Code zu meinem Problem (dieses Mal ohne REFs):

    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(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(string xmlText, 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, string xmlText, 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(nodeBegin);
    			while(foundCount < index) {
    				nBegin = GetNextPos(xmlText, 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(nodeEnd);
    
    			nEnd = GetNextPos(xmlText, 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(string xmlText, string masterNode, int mnIndex, 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, xmlText, 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, xmlText, 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;
    		}
    	}
    

    Wer das Problem aus nächster Nähe sehen will kann sich hier ein Demo Programm herunterladen. Klickt einfach mal auf den Button "GetNodes" und wählt dann einen Knoten aus der Liste aus. Bei einigen wird der korrekte Wert angezeigt, bei anderen nicht. Wieso? 😕



  • Hi,

    internal static

    schon mal was von oop gehört ?
    warum baust du das Konzept nicht objektorientiert auf ?

    Du benutzt eine objektorientierte Sprache und versuchst auf Biegen und Brechen C zu programmieren.

    *Kopfschüttel*



  • Könnten wir uns vielleicht mal auf das wirkliche Problem konzentrieren - wieso finde ich das Ende eines Knotens nicht?
    Aber OK ich kann ja aus meinem NodeHandler eine Basisklasse machen, das löst mein kleines Problem aber trozdem nicht.


Anmelden zum Antworten