Ankündigung

Einklappen
Keine Ankündigung bisher.

Frage zur Performance von find()

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Frage zur Performance von find()

    Der Folgende Code parst einen String, z.B. diesen $010;20;4;14;23;4;$
    Die erste Grafik zeigt im roten Rahmen die Auslastung für diesen Block und 4 weitere Blöcke mit gleichem Code.

    Wie im Code zu sehen, wird immer erst die Startposition festgelegt (0 oder altes Ende) und die Endposition per find() gesucht. Hier also 1. Strichpunkt, 2. Strichpunkt, ... bis zum letzten Strichpunkt. Es wird immer wieder von vorne angefangen zu suchen, die Suche überspringt also für den 2. Strichpunkt den 1., für den 3. wird 1 und 2 übersprungen etc. -> die Suche dauert immer länger.

    [highlight=epc]
    if Data_Fehler1!=$$ then \\
    /* Parsen von Fehler 1 */; \\
    /* Parsen von WP_Fehler_1_Code */; \\
    Name_StartPos=0u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    WP_Fehler_1_Code=split(Data_Fehler1,Name_StartPos, Name_EndPos); \\
    /* Parsen von Datum 1 */; \\
    Name_StartPos=Name_EndPos+2u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,1u16)-1u16; \\
    Fehlertag=split(Data_Fehler1,Name_StartPos,Name_En dPos); \\
    Name_StartPos=Name_EndPos+2u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,2u16)-1u16; \\
    Fehlermonat=split(Data_Fehler1,Name_StartPos,Name_ EndPos); \\
    Name_StartPos=Name_EndPos+2u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,3u16)-1u16; \\
    Fehlerjahr=split(Data_Fehler1,Name_StartPos,Name_E ndPos); \\
    Jahresanpassung=convert(Fehlerjahr,0u16); \\
    Jahresanpassung=2000u16+Jahresanpassung; \\
    Fehlerjahr=convert(Jahresanpassung,$$); \\
    WP_Fehler_1_Datum=Fehlertag+$.$+Fehlermonat+$.$+Fe hlerjahr; \\
    /* Parsen von Uhrzeit 1 */; \\
    Name_StartPos=Name_EndPos+2u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,4u16)-1u16; \\
    Fehlerstunde=split(Data_Fehler1,Name_StartPos,Name _EndPos); \\
    Name_StartPos=Name_EndPos+2u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,5u16)-1u16; \\
    Fehlerminute=split(Data_Fehler1,Name_StartPos,Name _EndPos); \\
    Minutenanpassung=convert(Fehlerminute,0u16); \\
    if Minutenanpassung<=9u16 then \\
    Fehlerminute=$0$+Fehlerminute \\
    endif; \\
    WP_Fehler_1_Uhrzeit=Fehlerstunde+$:$+Fehlerminute \\
    endif;\\
    [/highlight]

    Dieses "Problem" habe ich nun umschifft, indem ich den ersten Endpunkt suche, den Teilstring dann aber abschneide und im nächsten Schritt wieder nur den 1. Strichpunkt suche.
    Es wird also die Suche per find() gegen das Kürzen des Strings getauscht. Zusätzlich fällt eine Zuweisung weg, da der Start immer bei 0 ist.

    Die geänderte Auslastung wird in der 2. Grafik gezeigt.
    Auch hier sind im roten Rahmen insgesamt 5 solcher Blöcke gezeigt.

    [highlight=epc]
    /* Parsen der einzelnen Fehler */; \\
    if Data_Fehler1!=$$ then \\
    /* Parsen von Fehler 1 */; \\
    /* Parsen von WP_Fehler_1_Code */; \\
    Name_StartPos=0u16; \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    WP_Fehler_1_Code=split(Data_Fehler1,Name_StartPos, Name_EndPos); \\
    Data_Fehler1=split(Data_Fehler1,Name_EndPos+2u16,E ND);\\
    /* Parsen von Datum 1 */; \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    Fehlertag=split(Data_Fehler1,Name_StartPos,Name_En dPos); \\
    Data_Fehler1=split(Data_Fehler1,Name_EndPos+2u16,E ND); \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    Fehlermonat=split(Data_Fehler1,Name_StartPos,Name_ EndPos); \\
    Data_Fehler1=split(Data_Fehler1,Name_EndPos+2u16,E ND); \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    Fehlerjahr=split(Data_Fehler1,Name_StartPos,Name_E ndPos); \\
    Data_Fehler1=split(Data_Fehler1,Name_EndPos+2u16,E ND); \\
    Jahresanpassung=convert(Fehlerjahr,0u16); \\
    Jahresanpassung=2000u16+Jahresanpassung; \\
    Fehlerjahr=convert(Jahresanpassung,$$); \\
    WP_Fehler_1_Datum=Fehlertag+$.$+Fehlermonat+$.$+Fe hlerjahr; \\
    /* Parsen von Uhrzeit 1 */; \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    Fehlerstunde=split(Data_Fehler1,Name_StartPos,Name _EndPos); \\
    Data_Fehler1=split(Data_Fehler1,Name_EndPos+2u16,E ND); \\
    Name_EndPos=find(Data_Fehler1,$;$,0u16)-1u16; \\
    Fehlerminute=split(Data_Fehler1,Name_StartPos,Name _EndPos); \\
    Minutenanpassung=convert(Fehlerminute,0u16); \\
    if Minutenanpassung<=9u16 then \\
    Fehlerminute=$0$+Fehlerminute \\
    endif; \\
    WP_Fehler_1_Uhrzeit=Fehlerstunde+$:$+Fehlerminute; \\
    endif;\\
    [/highlight]

    Der Unterschied scheint erheblich zu sein. Teilweise 250ms weniger pro Block.

    Vermutlich hat das find() ein Problem. Könnt Ihr bei Ennertex prüfen, warum der 2. Code so viel schneller ist und ob hier tatsächlich das find() die Ursache ist?
    Angehängte Dateien
    BR
    Marc

    #2
    Zitat von saft6luck Beitrag anzeigen
    Vermutlich hat das find() ein Problem. Könnt Ihr bei Ennertex prüfen, warum der 2. Code so viel schneller ist und ob hier tatsächlich das find() die Ursache ist?
    Das ist sehr gut ausgetüfelt! Respekt.
    Ich geb das weiter. Find() ruft, wenn ich das recht in Erinnerung habe, einfach eine C-Funktion strcmp() auf, ggf. auch mehrfach. Daher kann es sein, dass die Zeiten sich addieren und es tatsächlich besser ist, die Strings abzuschneiden. Einen Bug an sich vermute ich nicht.
    offizielles Supportforum für den Enertex® EibPC: https://knx-user-forum.de/eibpc/
    Enertex Produkte kaufen

    Kommentar


      #3
      Zitat von saft6luck Beitrag anzeigen
      Dieses "Problem" habe ich nun umschifft, indem ich den ersten Endpunkt suche, den Teilstring dann aber abschneide und im nächsten Schritt wieder nur den 1. Strichpunkt suche.
      Es wird also die Suche per find() gegen das Kürzen des Strings getauscht. Zusätzlich fällt eine Zuweisung weg, da der Start immer bei 0 ist.
      Hey Marc!

      Sehr gut! Die Idee hatte ich auch schon, wurde jedoch an Mangel an Zeit nie realisiert..

      Grüße
      Matthias

      Kommentar


        #4
        Zitat von saft6luck Beitrag anzeigen
        Vermutlich hat das find() ein Problem. Könnt Ihr bei Ennertex prüfen, warum der 2. Code so viel schneller ist und ob hier tatsächlich das find() die Ursache ist?
        Wir haben das intensiv anhand des Makros für die Wunderground analysiert.
        Hier fällt einiges auf:
        Das Wundergroundmakro wurde mit einem 65k String definiert. Hierzu:
        • splits bis END kopieren wirklich alle Bytes (egal ob Nullbytes vorhanden oder nicht).
        • finds fangen immer beim Anfang des Strings an.
        • Stringzuweisungen sind Kopiervorgänge

        Wounderground liefert etwa 16K byte an Wetterdaten als Antwort auf die Anfrage. Daher sind die 65K-Strings schon mal 4 fach überdimensioniert. Jeder copy, split oder find benötigt entsprechend 4x länger, als notwendig. Ein Copy über 64kByte mit Overhead der FW benötigt schon mal mehre hundert µs, für den find-Befehl sogar wenige ms (je nachdem wann der find-Befehl einen "Treffer" hat).
        Im Wondergroundmakro "Wettervorhersage" wurden sämtliche Anweisungen in einem Wisch geparst. Das waren 140 finds, 99 splits. Dadurch kommt eine Stoßlast von bis zu 1800ms zustande. Das bedeutet dann aber auch, das z.B. cycle nicht mehr sauber arbeitet, auch after (siehe Beobachtung von amazing (https://knx-user-forum.de/eibpc/3467...gleich-ms.html)) etc. Da after nicht mehr ordentlich arbeitet, ist ab 800ms obige Anzeige und die Analyse mit obigem Code auch mit Vorsicht zu genießen. Insbesondere dann, wenn es scheint, dass die Abfrage, die eigentlich immer gleich dauern muss, manchmal 900ms, dann nur 300ms etc. braucht. Es ist dann nicht die NSA, wie bmx vermutet, sondern schlicht die Analyse nicht mehr brauchbar.
        Wir werden eine Funktion processingtime() einbauen, um hier verlässlicher und einfach arbeiten zu können. Ggf. bauen wir auch einen event ein, sodass es für den Support einfacher wird.

        Die Lösung des ursprünglichen Problems liegt einfach in der Aufspaltung der Abfragen. Steffi hat das nun für das besagte Makro gemacht und getestet und die Abfragen in 11 Teile zerlegt. Das sollte grundsätzlich bei allen Usermakros beachtet werden.
        offizielles Supportforum für den Enertex® EibPC: https://knx-user-forum.de/eibpc/
        Enertex Produkte kaufen

        Kommentar


          #5
          Sehr gut.
          Und woher bekommen wir die verbesserte Version des Makros oder wann wird eine neue Makro Lib freigegeben?

          Gruß

          Quisa

          Kommentar


            #6
            Zitat von enertegus Beitrag anzeigen
            Wir haben das intensiv anhand des Makros für die Wunderground analysiert.
            Hier fällt einiges auf:
            Das Wundergroundmakro wurde mit einem 65k String definiert. Hierzu:
            • splits bis END kopieren wirklich alle Bytes (egal ob Nullbytes vorhanden oder nicht).
            • finds fangen immer beim Anfang des Strings an.
            • Stringzuweisungen sind Kopiervorgänge

            Wounderground liefert etwa 16K byte an Wetterdaten als Antwort auf die Anfrage. Daher sind die 65K-Strings schon mal 4 fach überdimensioniert. Jeder copy, split oder find benötigt entsprechend 4x länger, als notwendig. Ein Copy über 64kByte mit Overhead der FW benötigt schon mal mehre hundert µs, für den find-Befehl sogar wenige ms (je nachdem wann der find-Befehl einen "Treffer" hat).
            Das war mir z.B. nicht klar, dass immer der gesamte String kopiert wird. Mir war schon aufgefallen, dass der eibPC ja irgendwie auf den Fall reagieren muss, wenn man in einen "leeren" String irgendwo ein Zeichen reinschreibt (-> daher dachte ich mir, dass der String schon vollständig im Speicher vorhanden ist). Es ist also eine statische Lösung.

            Wenn es also keine "Längeninformation" gibt, wird immer komplett umkopiert, auch klar.

            => Im WP-Makro habe ich alle Strings nun von 200 Zeichen auf 100 Zeichen reduziert. Bei solchen kurzen Strings scheint es noch keinen großen Effekt zu haben.

            Im Wondergroundmakro "Wettervorhersage" wurden sämtliche Anweisungen in einem Wisch geparst. Das waren 140 finds, 99 splits. Dadurch kommt eine Stoßlast von bis zu 1800ms zustande. Das bedeutet dann aber auch, das z.B. cycle nicht mehr sauber arbeitet, auch after (siehe Beobachtung von amazing (https://knx-user-forum.de/eibpc/3467...gleich-ms.html)) etc.
            Ok, deckt sich mit der Implementierung im WP-Makro (nur sind hier die Strings um Faktoren kleiner).

            Da after nicht mehr ordentlich arbeitet, ist ab 800ms obige Anzeige und die Analyse mit obigem Code auch mit Vorsicht zu genießen. Insbesondere dann, wenn es scheint, dass die Abfrage, die eigentlich immer gleich dauern muss, manchmal 900ms, dann nur 300ms etc. braucht. Es ist dann nicht die NSA, wie bmx vermutet, sondern schlicht die Analyse nicht mehr brauchbar.
            EDIT: Das Problem mit der Ausführung des "Messcodes" bei Überlasst hatte ich ja auch schon festgestellt. Daher ist jeder Schritt im Code 3 Sekunden auseinander -> Messung der Last rauf, Last runter, Pause. Und zusätzlich habe ich strikt darauf geachtet, dass wirklich jeder Messwert innerhalb der 3 Sekunden erfasst wurde. Bei Auftreten einer Überlasst würde ein Messwert erst nach einer längeren Pause aufgezeichnet werden (der dann auch noch falsch wäre).

            Hier ist also wirklich der Zustand vor und nach der Optimierung zu sehen. Da geht es nicht um "manchmal", sondern "immer". Der gewählte Ausschnitt ist nur extra für diese Abbildung ab Start des eibPCs aufgenommen, um die Maxima auch sehen zu können. Jeder andere Zeitpunkt wird genauso wieder dargestellt.

            Wir werden eine Funktion processingtime() einbauen, um hier verlässlicher und einfach arbeiten zu können. Ggf. bauen wir auch einen event ein, sodass es für den Support einfacher wird.
            Super!!!

            Die Lösung des ursprünglichen Problems liegt einfach in der Aufspaltung der Abfragen. Steffi hat das nun für das besagte Makro gemacht und getestet und die Abfragen in 11 Teile zerlegt. Das sollte grundsätzlich bei allen Usermakros beachtet werden.
            Dies war beim WP-Makro ja auch die Lösung! Der Vollständigkeit halber werde ich meine Version demnächst hier veröffentlichen.

            Viele Dank für die Analyse!

            Noch ein Punkt: Die Argumentation bei find() mag schon richtig sein, die Implementierung von find() ist aber (offensichtlich?) nicht gut, denn ein find() darf nicht so lange dauern.

            Meine Vermutung ist, dass find() als eigener Prozess gestartet wird und bei "Mehrfach-Suche" für jedes Auftreten erneut (sequenziell) aufgerufen wird. Da das Userprogramm Priorität genießt und das find() die Verarbeitung so blockiert, wird schon bei kleinsten Strings (und es geht beim WP-Makro um 2-5 Bytes) die Suche zum Problem. Dies würde zumindest die lange Ausführungszeit erklären.

            Egal wie, da es keine wirklichen Schleifen gibt, ist find() ein essenzielles Kommando, das nicht zu derartigen Verzögerungen führen darf (sollte). Notfalls muss man auf die C-Bibliothek verzichten und es per Hand implementieren etc.

            Habt ihr denn keine Möglichkeit da was zu verbessern?
            BR
            Marc

            Kommentar


              #7
              Zitat von QuisaZaderak Beitrag anzeigen
              Sehr gut.
              Und woher bekommen wir die verbesserte Version des Makros oder wann wird eine neue Makro Lib freigegeben?
              Dich interessiert die Wunderground-Lib, oder?
              BR
              Marc

              Kommentar


                #8
                Zitat von saft6luck Beitrag anzeigen
                Wenn es also keine "Längeninformation" gibt, wird immer komplett umkopiert, auch klar.
                Split hat schon eine Längeninformation, nur wird eine terminierende Null nicht beachtet - was man wohl auch nicht will, wenn man die Strings als Array betrachtet.
                => Im WP-Makro habe ich alle Strings nun von 200 Zeichen auf 100 Zeichen reduziert. Bei solchen kurzen Strings scheint es noch keinen großen Effekt zu haben.
                Klar, wir reden ja von 65000 und nicht 100 Zeichen.
                Noch ein Punkt: Die Argumentation bei find() mag schon richtig sein, die Implementierung von find() ist aber (offensichtlich?) nicht gut, denn ein find() darf nicht so lange dauern.
                Da bin ich nicht so sicher. Ich denke strcmp ist weitgehend optimiert. Da spielt eben die Hardware eine Rolle (SDRAM, Cache, etc.), aber auch zum Betriebssystem (Memory allocation ...). Am Ende ist dann ja noch das FW-Framework dabei, was aber nun schon sehr ausgeklügelt ist (Valdierung...)
                Notfalls muss man auf die C-Bibliothek verzichten und es per Hand implementieren etc.
                Habt ihr denn keine Möglichkeit da was zu verbessern?
                Man kann alles verbessern. Die Frage ist, ob sich der Aufwand lohnt. Wenn man die Monstermakros entsprechend "designed", ist dies das nicht notwendig, wie ja nun gezeigt ist. Man muss eben wirklich große Audgaben etwas entzerren. Letztlich sind einige Usermakros mittlerweile wirkliche aufwändige Softwarekonstrukte, wo man eben auch was beachten muss - und wir müssen erst mal den Rahmen finden. Daher auch diese wirklich intensive Recherche.
                offizielles Supportforum für den Enertex® EibPC: https://knx-user-forum.de/eibpc/
                Enertex Produkte kaufen

                Kommentar


                  #9
                  Zitat von enertegus Beitrag anzeigen
                  Split hat schon eine Längeninformation, nur wird eine terminierende Null nicht beachtet - was man wohl auch nicht will, wenn man die Strings als Array betrachtet.
                  Gemeint war die Längeninformation für Strings. Es gibt ja sizeof(), welches nicht die maximal mögliche Länge, sondern die verwendete Länge ausgibt. Daher hätte ich vermutet, dass es für solche (binären) Strings eine Längeninformation gibt, die man dann auch nutzen kann.

                  Da bin ich nicht so sicher. Ich denke strcmp ist weitgehend optimiert.
                  NB:
                  - Für binäre Strings ist die C-Bibliothek auch nicht gedacht (da sie ja auf die 0-Terminierung achten), zumal man strstr() bzw. mit V3.x wohl wcsstr() verwenden sollte.
                  - Strings will man eigentlich nicht als Arrays betrachten
                  - Die Zeit der 0-terminierten Strings ist eigentlich schon wieder vorbei.

                  Da spielt eben die Hardware eine Rolle (SDRAM, Cache, etc.), aber auch zum Betriebssystem (Memory allocation ...). Am Ende ist dann ja noch das FW-Framework dabei, was aber nun schon sehr ausgeklügelt ist (Valdierung...)
                  Da solltest du eben nicht aufhören nach der Lösung zu suchen. Die Prozessoren, mit denen ich arbeite, sind wesentlich langsamer und trotzdem benötigt ein strstr() bei derart kleinen Strings nur wenige us.

                  Schau dir die Relationen doch bitte mal an. Bei 10 Zeichen und einem einzigen Suchzeichen könnte man ja schon fast im eibPC per Hand schneller suchen.

                  Man kann alles verbessern. Die Frage ist, ob sich der Aufwand lohnt. Wenn man die Monstermakros entsprechend "designed", ist dies das nicht notwendig, wie ja nun gezeigt ist. Man muss eben wirklich große Audgaben etwas entzerren. Letztlich sind einige Usermakros mittlerweile wirkliche aufwändige Softwarekonstrukte, wo man eben auch was beachten muss - und wir müssen erst mal den Rahmen finden. Daher auch diese wirklich intensive Recherche.
                  Nur mal laut gedacht:

                  1. Das Entzerren solcher Makros ist ein enormer Aufwand, der von der Sprache nicht unterstütz wird. Diesbezüglich hatte ich vor ca. 1/2 Jahr mal wesentliche Verbesserungen vorgeschlagen, nur ein paar Basics. Eine Entzerrung per after() ist alles andere als robust!

                  2. Eine zentrale Funktion, hier das find(), muss so schnell sein, dass man damit arbeiten kann. Wenn dies nur über die gezeigten Umwege (suchen, umkopieren, suchen, ...) akzeptabel funktioniert, muss man eben dieses Vorgehen als Kommando implementieren. (Wenn man find() wirklich nicht anders optimieren kann).

                  3. Die Stabilität des eibPCs hat Vorrang! Wenn Usermakros den eibPC lahmlegen können ist keinem geholfen. Die Probleme sind nun bekannt, die Lösung fängt aber bei der Abarbeitung des Codes an. Du willst jetzt eine verlässliche Funktion für die Performace-Messung einbauen, gut! Dann der Event (für die Signalisierung der Überlast?), ok.
                  Dann muss der Programmierer wieder in endlosen Sitzungen den Code zerstückeln? Allein schon den Code neu starten und auf die ersten Aufrufe warten dauert schon mehrere Minuten! Warum nicht gleich entsprechende Sektionen mit niedriger Priorität? Wen interessiert, ob ein Wonderground-Makro 2 Sekunden später fertig ist? Mich interessiert aber sehr wohl, dass meine Rollos runterfahren. Und wer garantiert, dass ein Makro mit einem anderen zusammen sicher keine Überlast produziert?

                  IMHO muss die Performance-Messung ein zentrales Instrument werden, das man nicht erst einbauen muss.
                  D.h. nicht, dass das nicht dauern darf
                  BR
                  Marc

                  Kommentar

                  Lädt...
                  X