Syntax Highlighting zu langsam. Aktualisierung unterdrücken möglich, sonstige ideen?

  • VB.NET

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von picoflop.

    Syntax Highlighting zu langsam. Aktualisierung unterdrücken möglich, sonstige ideen?

    Hallo zusammen,

    ich habe angefangen ein wenig rumzuprobieren ob ich nen gescheiten Editor hinkriege und probiere mich nun am Syntax Highlighting.

    Ich hab nun auch schon ettliche Variationen und bin auch schon um 60% Schneller als Anfangs doch immer noch viel zu langsam.

    ich habe eine RichTextbox in der ich text eintippen bzw reinkopieren kann. Nach betätigen von z.B. Enter wird die komplette Textbox nach dem Wort "Auto"(ist in einem String Array gespeichert) gesucht.
    Ich speichere mir die Startpos von jedem gefunden Wort in ein Array.
    Nun kommt noch eine Schleife die die gefundenen Positionen durchläuft und dann jedes Wort Markiert und mit der Farbe Rot einfärbt.
    Klappt eigentlich sehr gut, bis auf nen kleinen Bug, aber eben die Geschwindigkeit. Pfui !
    Für 300 Zeilen Text mit ca .12000 zeichen und einigen "Auto"s darin versteckt läuft die Sache gut über 2 Sekunden. Wie krieg ich das schneller hin ?
    Hier mal die wichtigen Ausschnitte:

    VB.NET-Quellcode

    1. Private Sub rtxtEingabe_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles rtxtEingabe.KeyDown
    2. ' Testreihe 3
    3. Dim AuswahlStr As Integer = 0
    4. Dim FindPosArray(1000) As Integer
    5. Dim Nr As Integer = 0
    6. If e.KeyCode = Keys.Space Or e.KeyCode = Keys.Enter Or e.KeyCode = Keys.Tab Then ' Reagiert auf bestimmte Tasten
    7. StopWatch.Start()
    8. Dim CaretMerker As Integer ' Positionsmerker für Caretposition(Position= akt. Zeichen)
    9. Dim i As Integer ' Schleifenzaehler
    10. CaretMerker = rtxtEingabe.SelectionStart
    11. For i = 0 To rtxtEingabe.Text.Length' Positionen der gefunden Wörter speichern
    12. If rtxtEingabe.Find(SuchStr(AuswahlStr), i, i + SuchStr(AuswahlStr).Length + 1, RichTextBoxFinds.None) >= 0 Then
    13. FindPosArray(Nr) = i + 1
    14. i = i + SuchStr(AuswahlStr).Length
    15. Nr = Nr + 1
    16. End If
    17. Next
    18. For i = 0 To Nr - 1 ' Wörter markieren und färben
    19. rtxtEingabe.SelectionStart = FindPosArray(i)
    20. rtxtEingabe.SelectionLength = SuchStr(AuswahlStr).Length
    21. rtxtEingabe.SelectionColor = Color.Red
    22. rtxtEingabe.SelectionLength = 0
    23. rtxtEingabe.SelectionStart = CaretMerker
    24. StartPos = CaretMerker
    25. rtxtEingabe.SelectionColor = Color.Black
    26. Next
    27. StopWatch.Stop()
    28. lbl_DEBUG_Stopp.Text = StopWatch.ElapsedMilliseconds.ToString
    29. StopWatch.Reset()
    30. End If


    Ich habe jetzt 2 Ideen zur Optimierung:
    1) Mehrere Wörter gleichzeitig markieren, nicht hintereinander in ner Schleife wie bisher.
    oder
    2) Irgendwie die aktualisierung der Textbox aufhalten und erst wenn alle Wörter sozusagen im Hintergrund gefärbt wurden die Anzeige refreshen.

    Bei beidem gilt: Wie anstellen?
    Hoffe ihr habt ein paar gute Tipps auf Lager.


    MfG CodeB
    Schade dass Ihr keine Tipps für mich habt, aber vielleicht gibts nocn ein paar Ratschläge zu folgendendem:
    Die Ausführzeit für den geposteten Code ist auch stark abhängig davon ob sich mein Mauscursor innerhalb des Forms/ innerhalb der Textbox oder außerhalb von allem befindet.
    Wenn mein Cursor in der Textbox steht ist die Ausführzeit um bis zu 30% langsamer. Das hängt wohl damit zusammen dass der Compiler ja dann auch die Position und Ereignisse des Cursors bearbeiten muss bei jedem Schleifendurchlauf.
    Was kann ich tun dass dieser Effekt nicht mehr auftritt ?
    Kann ich vielleicht verhindern dass auf Events und Nachrichten von bestimmten Elementen (wie dem Cursor)nicht reagiert wird?

    Vielleicht habt Ihr ja hierzu ein paar Ideen.

    Bis dann,
    CodeB
    Schau dir mal .SuspendLayout und .ResumeLayout an. Du solltest mE das neuzeichnen der RTB während (!) der Änderungen unterdrücken. Konnte evtl etwas Speed bringen. Auf jeden Fall würde ich mal benchen, wie lange das suchen und wie lange das ersetzen dauert. Also stopwatch für beide Hauptblöcke mal laufen lassen.
    Danke schonmal @Pico.
    Habe jetzt nochmal die Zeit gemessen, nachdem ich auch meinen erwähnten Bug gefixed habe. Für die 300 Zeilen ist die Gesamtzeit bei ca. 2900 ms. Dabei benötigt nur das Suchen schon ca. 1000 ms. Wenn der Mauszeiger in der RTB ist, liegt die Gesamtzeit sogar bei 3800ms also fast 1 Sekunde mehr.

    CodeBlacky schrieb:

    Dabei benötigt nur das Suchen schon ca. 1000 ms

    Schau mal, ob du beim Suchen nicht vlt besser auf RegEx umsteigst. RegEx ist langsam, wenn man es nur einmal verwendet, aber dürfte schneller sein, wenn man es mehrmals anwendet.
    Evtl hilft auch, wenn du den String in ein Array kopierst (Encoding beachten) und dann byte- bzw wort (16 bit, UTF) weise selber suchst. Die gefundenen Positionen entsprächen ja dann der Position im rtb.text.

    Bzgl. einfärben: Ich würde mir überlegen, ob ne RTB überhaupt das richtige ist. Du hast zwar dreihundert Zeilen, aber der User wird die wohl nicht alle sehen. Statt also das einfärben dann vorzunehmen, wenn der String komplett geladen wird, würde ich immer nur den Teil bearbeiten, der sichtbar ist und noch nicht bearbeitet wurde. Weiß jetzt aber nicht, ob das mit ner RTB machbar ist.
    Ist es überhaupt notwendig, wenn die gewissen Tasten gedrückt wurden (Zeile 7), den GANZEN Code neu einzufärben (Zeile 15)?
    Ein anderern Ansatz (Wie bei der VB-Entwicklungsumgebung selbst) wäre es, beim Anzeigen der RTB den ganzen Code, beim Editieren aber nur die jeweils aktuelle Zeile zu bearbeiten - und umzufärben.
    @der Kurt
    Du hast natürlich recht. Es ist überhaupt nicht nötig auf tastendruck den gesammten Text zu durchforsten. Ich hab das jetzt nur für die ersten Forschungszwecke so gemacht. Stell dir einfach vor der Tastendruck "Space" simuliert das öffnen des formulars bzw. einer Textdatei mit den genannten 300 Zeilen Text.

    @picoflop
    Danke! Hast mir schon mal ein paar gute Denkanstöße gegeben werde mich gleich mal ran machen die ersten Versuche zum Laufen zu bringen. Sehr Sinnvoll ist natürlich schonmal nur die sichtbaren Zeilen zu durchsuchen. Aber ich glaube auch das Durchsuchen im Hintergrund in einem Array könnte vielleicht was bringen zu den RegEx habe ich bisher leider keinerlei Erfahrung. Aber das werde ich jetzt dann wohl mal ändern.


    Ich werde euch auf dem Laufenden halten, was ich die Tage noch so hinbekomme.
    Habse mal probiertse ...

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private stp As New Stopwatch
    3. Private Sub FindBench()
    4. Dim l As New List(Of Integer)
    5. stp = Stopwatch.StartNew
    6. l = FindRTB("auto", RichTextBox1)
    7. stp.Stop()
    8. Debug.Print("FindRTB: " & l.Count & " = " & stp.ElapsedMilliseconds)
    9. stp = Stopwatch.StartNew
    10. l = FindWithRegex("Auto", RichTextBox1)
    11. stp.Stop()
    12. Debug.Print("FindWithRegex: " & l.Count & " = " & stp.ElapsedMilliseconds)
    13. End Sub
    14. Private Function FindRTB(ByVal srch As String, ByVal rtb As RichTextBox) As List(Of Integer)
    15. Dim l As New List(Of Integer)
    16. Dim i As Integer = srch.Length
    17. Dim j As Integer
    18. Do
    19. j = rtb.Find(srch, j, RichTextBoxFinds.None)
    20. If j = -1 Then Exit Do
    21. l.Add(j)
    22. j += i
    23. Loop Until j >= rtb.Text.Length
    24. Return l
    25. End Function
    26. Private Function FindWithRegex(ByVal srch As String, ByVal rtb As RichTextBox) As List(Of Integer)
    27. Dim l As New List(Of Integer)
    28. Dim i As Integer = srch.Length
    29. Dim j As Integer
    30. Dim c As System.Text.RegularExpressions.MatchCollection = _
    31. System.Text.RegularExpressions.Regex.Matches(rtb.Text, srch, _
    32. System.Text.RegularExpressions.RegexOptions.Compiled Or _
    33. System.Text.RegularExpressions.RegexOptions.IgnoreCase Or _
    34. System.Text.RegularExpressions.RegexOptions.Multiline)
    35. l.AddRange((From m As System.Text.RegularExpressions.Match In c Select m.Index).ToArray)
    36. Return l
    37. End Function
    38. Private Sub BtnFill_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnFill.Click
    39. Dim rn As New Random
    40. Dim i As Integer = 0
    41. Dim a As Integer = 0
    42. Dim sb As New System.Text.StringBuilder
    43. Dim r As Integer
    44. Do
    45. r = rn.Next(0, 20)
    46. Select Case r
    47. Case 0, 1
    48. sb.AppendLine()
    49. Case 2
    50. sb.Append("Auto" & i.ToString)
    51. a += 1
    52. Case Else
    53. sb.Append("Banane" & i.ToString)
    54. End Select
    55. i += 1
    56. Loop Until sb.Length > 120000
    57. RichTextBox1.Clear()
    58. RichTextBox1.Text = sb.ToString
    59. Debug.Print(a & " Autos!")
    60. End Sub
    61. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    62. FindBench()
    63. End Sub
    64. End Class


    Bei 120.000 Zeichen, kommt der RTB.Find auf rund 7.000 ms und Regex auf 100. Also Faktor 70 ... Die absoluten Zeiten sind natürlich rechnerabhängig, aber das Verhältnis sollte in etwa gleich bleiben. Kann man vermutlich noch irgendwie optimieren, aber das ist dann jedem selbst überlassen.

    EDIT. War wohl überlastet. Jetzt braucht der Regex ziwschen 9 (!) und 55 ms ... ;)


    Highlight Funktion bei selben Voraussetzungen rund 600 ms
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub Highlight(ByVal rtb As RichTextBox, ByVal l As List(Of Integer), ByVal srch As String)
    2. rtb.SuspendLayout()
    3. Dim ln As Integer = srch.Length
    4. For Each j As Integer In l
    5. rtb.SelectionStart = j
    6. rtb.SelectionLength = ln
    7. rtb.SelectionColor = Color.Red
    8. rtb.SelectionLength = 0
    9. Next
    10. rtb.ResumeLayout()
    11. End Sub


    Ist für Syntaxhighlighting aber alles irgendwie der falsche ansatz. Da müsste man nicht prüfen, ob der Befehl im Text ist, sondern für jedes "Wort" des Textes prüfen, ob es ein Befehl ist. Denn Befehle gibt's ja meist mehr als einen ...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „picoflop“ ()

    Wow !!!
    Da kann man ja durchaus von Optimierung sprechen. Klasse!
    Das sieht Zeitlich ja schon echt gut aus. Wobei ich dir zustimme dass dies evtl ein komplett falscher Ansatz ist für Syn. Highlighting. Zu dem Punkt bin ich schon am Anfang immer wieder gekommen. Da ich erstmal auf keine andere Lösung kam(die ICH hätte lösen können)dachte aber ich probier trotzdem Mal in die Richtung obs nicht doch ganz gut funktioniert.

    Danke für deine Mühe! :thumbsup: