[C#] Code-Optimierung möglich?

kalleberlin

Lieutenant
Registriert
Feb. 2005
Beiträge
853
Hallo liebe F´bler :)

Diesmal möchte ich euch nicht mit Syntaktischen fehlern "bewerfen", nein diesesmal bräuchte ich ein wenig Nachhilfe in "Performance" Programmierung.

Worum gehts?

Ich möchte eine eigentlich relativ kleine Datei mit ~30k Zeilen nach bestimmten kriterien Auswerten. Um welche kriterien es sich handelt, das bestimmt der Benutzer selber. Die kriterien ansich, definieren eigentlich nichts anderes als bestimmte Strings.

Wenn der Benutzer aber nun entscheided, sich diese ganze Datei an einem Stück anzusehen, dauert das komischerweise relativ lange (wir reden von ~30 sekunden).

Was mache ich?

Hier mal mein Code (gekürzt aufs wesentliche :D):
PHP:
private void btnShowChat_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            dateTimeStart.CustomFormat = "HH:mm:ss";
            dateTimeStart.Format = DateTimePickerFormat.Custom;
            dateTimeStop.CustomFormat = "HH:mm:ss";
            dateTimeStop.Format = DateTimePickerFormat.Custom;
            Form2 server = new Form2();
            ChatLog chatwindow = new ChatLog();
            chatwindow.richChatLog.Clear();
            String sLine = "";
            String n = "\n";
            String Nick = Convert.ToString(listBox1.SelectedItem);
            String tempA = getStartTime();
            String tempB = getStopTime();
            tempA = tempA.Replace(":", "");
            tempB = tempB.Replace(":", "");
            int startTime = Convert.ToInt32(tempA);
            int stopTime = Convert.ToInt32(tempB);
            int currentTime = 0;
            int lines = 0;
            string bline = "";
            try
            {
                StreamReader fileb = new StreamReader(server.getServerLogPath() + "\\chat.log");
                while (bline != null)
                {
                    bline = fileb.ReadLine();
                    lines++;
                }
                StreamReader file = new StreamReader(server.getServerLogPath() + "\\chat.log");
                progressBar1.Maximum = lines;
                progressBar1.Step = 1;
                String Date = GetDate();
                while (sLine != null)
                {
                    sLine = file.ReadLine();
                    progressBar1.PerformStep();
                    //get time from current string
                    if (sLine != null && !sLine.Equals("") && checkCurrentTime.Checked == true)
                    {
                        
                        String[] timeInFile = sLine.Split(' ');
                        String time = timeInFile[2];
                        time = time.Replace("]", "");
                        time = time.Replace(":", "");
                        if (!time.Equals("")) 
                        currentTime = Convert.ToInt32(time);
                    }
                    if (checkShout.Checked == false && checkTrade.Checked == false 
                        && checkPrivate.Checked == false && checkAll.Checked == false
                        && checkParty.Checked == false && checkClan.Checked == false
                        && checkAlliance.Checked == false )
                    {
                        if (sLine != null && listBox1.SelectedItem == null)
                        {
                            if (sLine != null && sLine.Contains(Nick))
                                while (sLine != null)
                                {
                                    chatwindow.richChatLog.AppendText(sLine + n);
                                    sLine = file.ReadLine();
                                    progressBar1.PerformStep();
                                }
                        }
                    }
                    if (checkShout.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("SHOUT"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkShout.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("SHOUT") && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkTrade.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("TRADE"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkTrade.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("TRADE") && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkAll.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("ALL") && !sLine.Contains("ALLIANCE"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkAll.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("ALL") && !sLine.Contains("ALLIANCE") 
                            && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkPrivate.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("TELL"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkPrivate.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("TELL") && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkParty.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("PARTY"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkParty.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("PARTY") && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkClan.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("CLAN"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkClan.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("CLAN") && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkAlliance.Checked == true && checkDate.Checked == false)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("ALLIANCE"))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkAlliance.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && sLine.Contains("ALLIANCE") && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkCurrentTime.Checked == true && checkDate.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && currentTime >= startTime && currentTime <= stopTime 
                            && sLine.Contains(Date))
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                    if (checkCurrentTime.Checked == true)
                    {
                        if (sLine != null && sLine.Contains(Nick) && currentTime >= startTime && currentTime <= stopTime)
                            chatwindow.richChatLog.AppendText(sLine + n);
                    }
                }

                file.Close();
                progressBar1.Value = 0;
                this.Cursor = Cursors.Default;
                chatwindow.Show();
            }
            catch (FileNotFoundException ex)
            {

                MessageBox.Show(ex.Message);
            }
            catch (DirectoryNotFoundException exx)
            {
                MessageBox.Show(exx.Message);
            }
            
        }
Kann es sein das die richTextBox der langsame Teil ist? Denn sobald ich einfach mal die Datei "durchjage", sprich die Zeilen einfach nur mal kurz einlese, dauert das nichtmal 2 Sekunden.

Für Hilfe bin ich euch wie immer dankbar :)
 
Zuletzt bearbeitet: (//Quellcode vervollständigt)
1.) Ich weiß nicht, wie es in C# ist, aber bei Java ist eine TextArea immer extrem langsam vor allem, wenn viel drinsteht. Ich nehme an das liegt daran, weil der String immer beim Anhängen von ein paar Zeichen neu angelegt und kopiert wird. Ich würde sagen speichere alles, was du anhängen willst zuerst in einer Collection und zählst immer mit, wie lang es insgesamt ist. Dann legst du ein charArray mit der gewünschten Größe an und speicherst die Strings hinein. Das charArray hängst du dann in einem Zug an die RichtTextBox an.

2.) Sehe ich das richtig, dass du das File mehrere Male durchliest? Das ist ziemlich ineffizient, da der Filezugriff eher langsam ist. Die Platte mag zwar schnell sein, aber wenn du immer nur ein paar Zeichen auf einmal liest, dann hast du jedes Mal einen Context Switch, was CPU-Zeit benötigt. Ich würde sagen lies das File gleich in einem durch in den RAM, weil da kannst du viel schneller darauf zugreifen.

3.) Bei der Zeile: time = time.Replace("]", ""); schau am Besten nach, ob es nicht auch eine Funktion Remove gibt. Wenn ja, dann ist die wahrscheinlich etwas schneller.

4.) Bei Zeilen wie dieser:
if (sLine != null && sLine.Contains(Nick) && sLine.Contains("ALL") && !sLine.Contains("ALLIANCE"))
solltest du die Bedingungen nach ihrer Häufigkeit sortieren. Zuerst die Überprüfung auf NULL und dann das Wort, das am Seltensten vorkommt, dann das zweitseltenste usw. Damit steigt die Wahrscheinlichkeit, dass die Bedingung vorzeitig abbricht und die anderen Anweisungen nicht mehr durchgeführt werden müssen. Bei einer Oder Bedingung kommt die Wahrscheinlichste zuerst, wobei auch immer der Rechenaufwand berücksichtigt wird (arg1==NULL ist schneller als string.contains("abc") und sollte daher eher nach vorne)

5.) Du kannst teilweise die if-Bedingungen umstrukturieren:

if (checkTrade.Checked == true && checkDate.Checked == false)
{
if (sLine != null && sLine.Contains(Nick) && sLine.Contains("TRADE"))
chatwindow.richChatLog.AppendText(sLine + n);
}
if (checkTrade.Checked == true && checkDate.Checked == true)
{
if (sLine != null && sLine.Contains(Nick) && sLine.Contains("TRADE") && sLine.Contains(Date))
chatwindow.richChatLog.AppendText(sLine + n);
}


Daraus kannst du folgendes machen:

if(checkTrade.Checked == true && sLine != null && sLine.Contains(Nick) && sLine.Contains("TRADE") && (checkDate.Checked == false || sLine.Contains(Date))
chatwindow.richChatLog.AppendText(sLine + n);
 
Zum Zusammensetzen von Text gibt die Klasse System.Text.StringBuilder, die nicht für jeden Append-Vorgang den Puffer kopieren muss. Am Ende einfach .ToString() aufrufen und diesen String in das Textfeld schreiben
 
Erstmal danke für eure Antworten :)

Ziemlich viel Input für mich, deshalb nicht über meine nächsten Fragen lachen :D.

@andr_gin
zu 1.)
Ich würde sagen speichere alles, was du anhängen willst zuerst in einer Collection und zählst immer mit, wie lang es insgesamt ist. Dann legst du ein charArray mit der gewünschten Größe an und speicherst die Strings hinein. Das charArray hängst du dann in einem Zug an die RichtTextBox an.
Da komm ich nicht ganz mit, was meinst Du mit "Collection"?

zu 2.)
Wenn ich jetzt nicht ganz daneben liege, lese Ich die Datei nur ein einziges Mal durch (ok eigentlich 2 mal, aber das erste mal nur um die Zeilen zu zählen, was ziemlich genau 1,5 sekunden dauert, also vernachlässigbar ist).


zu 3.)
Ja, eine Remove Methode existiertst aber:
Mit time.Remove(); müsste ich die Position des Char´s angeben, woraus wieder resultiert das ich die ersteinmal Bestimmen müsste, also denke ich hebt sich das wieder auf mit der Geschwindigkeit.

zu4.)

Danke, werd ich mal ändern :)

zu 5.)

Ebenso :)

@Simpson474

Wie genau meinst Du das?
 
Das von mir ersetzt die vorgeschlagene Collection. Du erstellst vor der Schleife einen StringBuilder und fügst das, was du eigentlich mit "chatwindow.richChatLog.AppendText" eingefügt hättest, zum StringBuilder hinzu. Nach dem Abschluss aller Schleifendurchläufe wandelst du den StringBuilder in einer String um und fügst diesen String in das Textfeld ein (ca. so "chatwindow.richChatLog.AppendText(sb.ToString());")
 
Perfekt Simspon474 :)

Ich hab jetzt
PHP:
chatwindow.richChatLog.AppendText(sLine + n);
durch
PHP:
sb.AppendLine(sLine);
und am Ende
PHP:
chatwindow.richChatLog.AppendText(sb.ToString());

Ersetzt, und nun dauerts nur noch 2 Sekunden sich die ganze Datei anzeigen zu lassen :).

Fettes Danke :)

//edit:

Ich hab noch ein 2tes File mit ~ 300k zeilen, das geht ja ab wie ein Zäpfchen :D.
Echt geil :P
 
Zuletzt bearbeitet:
Im Schnitt ~ 80 Zeichen... Aber ist wirklich nur ungefähr, ist halt

[datum uhrzeit] [channel] [nickname] [chatText]
 
Ok, hab ein neues Performance Problem...

Es gibt ja in Visual C# die tolle Möglichkeite, einer textBox, eine Autovervollständigungsliste hinzuzufügen. Soweit sogut.

Ich mache in meinem Programm eine Datenbankabfrage, welche mir die Namen von sogenannten NPC´s zurückliefert. Diese fülle ich dann in eine Listbox, damit der Benutzer darauf zugreifen kann. Da wir hier aber von ~ 6000 Namen reden, kann es mitunter recht unübersichtlich werden. Im moment helfe ich mir so, dass wenn man jetzt einen Namen in die Textbox eingibt, dieser Name automatisch in der Listbox selektiert ist. Es gibt aber Namen die sich relativ ähnlich sind, und mitunter auch recht lang. Hier kommt nun die Autovervollständigung ins Spiel.

Im moment mach ich das so:
PHP:
String[] myStr = compleSource.Text.Split('\n');
                for (int i = 0; i < myStr.Length; i++)
                {
                    txtSearchNpc.AutoCompleteCustomSource.AddRange(new String[] {myStr[i]});
                }
Das dauert alledings ca 1-2 min bis die Liste gefüllt ist :(.

Ich habs auch schon direkt mit einem StringBuilder versucht, also direkt beim einlesen der Daten:
PHP:
while (Reader.Read())
                {
                    
                    string thisrow = "";
                    for (int i = 0; i < Reader.FieldCount; i++)
                    {
                        thisrow += Reader.GetValue(i).ToString();
                        if (!thisrow.Equals(""))
                        {
                        listBoxNpcs.Items.Add(thisrow);
                        sb.AppendLine(thisrow);

                        progressBar1.PerformStep();
                        }
                    }
    
                    
                }
                        txtSearchNpc.AutoCompleteCustomSource.Add(sb.toString());

Ich denke das würde schneller gehen, die liste ist dann allerdings leer :(.

Gibt es eine schnellere Möglichkeit das zu bewerkstelligen?

//edit:

Direkt so befüllen ist auch lahm...
PHP:
for (int i = 0; i < Reader.FieldCount; i++)
                    {
                        thisrow += Reader.GetValue(i).ToString();
                        if (!thisrow.Equals(""))
                        listBoxNpcs.Items.Add(thisrow);
                        txtSearchNpc.AutoCompleteCustomSource.Add(thisrow);
                        progressBar1.PerformStep();
                    }
 
Zuletzt bearbeitet:
Hast du schon mal
PHP:
txtSearchNpc.AutoCompleteCustomSource.AddRange(compleSource.Text.Split('\n'));
probiert?
 
Zuletzt bearbeitet:
Sorry, aber wenn du wirklich Power brauchst, dann schreib es in normalem C, nicht in C#.
 
Für seinen Zweck ist C# bei Weitem die bessere alternative und bei entsprechender Programmierung auch nicht merklich langsamer als reines C. Nur die Speicherbelastung ist deutlich höher.
 
@Simpson474
Du bist ein Goldjunge :D.
Wenn ich dich nicht hätte ;), funktioniert tadellos :).

@Lizzy2000
Sorry, aber das ist ne ziemlich unqualifizierte Aussage...
 
Jetzt rein vom Aussehen her sollte
PHP:
                List<string> list = new List<string>(); 
                while (Reader.Read())
                {
                    
                    string thisrow = "";
                    for (int i = 0; i < Reader.FieldCount; i++)
                    {
                        thisrow = Reader.GetValue(i).ToString();
                        if (!thisrow.Equals(""))
                        {
                              listBoxNpcs.Items.Add(thisrow);
                              list.Add(thisrow);

                              // Nächste Zeile könnte auch problematisch sein
                              progressBar1.PerformStep();
                        }
                    }
    
                    
                }
                txtSearchNpc.AutoCompleteCustomSource.AddRange(list);

am schnellsten sein.
 
Also ich habs jetzt so:
PHP:
while (Reader.Read())
                {
                    
                    string thisrow = "";
                    for (int i = 0; i < Reader.FieldCount; i++)
                    {
                        thisrow += Reader.GetValue(i).ToString();
                        if (!thisrow.Equals(""))
                        {
                            listBoxNpcs.Items.Add(thisrow);
                            sb.AppendLine(thisrow);
                            progressBar1.PerformStep();
                        }
                    }
    
                    
                }
                connection.Close();
                txtSearchNpc.AutoCompleteCustomSource.AddRange(sb.ToString().Split('\n'));
                progressBar1.Value = 0;
Und das befüllen der Liste merkt man nicht.

Warum meinst Du das der PerformanceStep(); problematisch sein könnte?
Überlege gerade wo ich den Balken noch woanders hochsetzen könnte...
 
Sorry, war nicht böse gemeint, aber Frauen C ist einfach langsamer als wenn man sowas "manuell" in Männer schreibt :lol:
 
Bei deiner jetzigen Lösung erstellst du erst einen sehr langen String, den du später wieder aufteilst. Ich denke du hast das nur gemacht um ein Array in dynamischer Größe zu bekommen. Und genau dafür ist eben die List<T> da, die ein "dynamisches Array" zur Verfügung stellt. String-Operationen sind grundsätzlich langsam. So wird zum Beispiel bei deinem .Split eine Schleife ausgeführt, die jedes einzelne Zeichen des Strings durchgeht. Dies kann man mit der List<T> verhindern.

Zum PerformStep: Wenn der Balken bei jedem Aufruf sofort neu gezeichnet wird, dann wäre die Performance nicht sehr gut. Ich weis jetzt nicht genau, wie das im .Net Framework gehandhabt wird.
 

Ähnliche Themen

Antworten
9
Aufrufe
2.341
Antworten
3
Aufrufe
974
Zurück
Oben