PerformanceTuning für Routine



  • Hallo zusammen,

    ich habe ein Programm, dass aus einer Email den Anhang ausliest und ihm auf die Festplatte speichert. Danach wird dieser Anhang geöffnet und verschiedene Werte ausgelesen.
    Der Inhalt ist in verschiedene Teile unterteilt. Im mittleren Teil sind die meisten Werte aufgelistet.
    Es werden Datensätze untereinander aufgelistet wobei jeder Datensatz wiederum aus mehreren Werten bestehen kann.
    Von diesen Datensätzen können nun einige untereinander aufgelistet sein (max. ca 150 )

    Bsp.:

    .......
    Anfang mittlerer Teil

    1)Wert1,String1,Wert2,String2,Wert3,String3,........,Wert(n),String(n);
    2)Wert1,String1,Wert2,String2,Wert3,String3,........,Wert(n),String(n);
    3)Wert1,String1,Wert2,String2,Wert3,String3,........,Wert(n),String(n);
    4)....
    .
    .
    .
    n)Wert1,String1,Wert2,String2,Wert3,String3,........,Wert(n),String(n);

    Ende mittlerer Teil
    .........

    Ich gehe nun her und lese mir aus dem ganzen Inhalt jeweils ein Datensatz aus. Aus diesem Datensatz lese ich dann mit while - Schleifen (und Pointerarithmetik) die einzelnen Werte aus.
    Danach werden diese Werte dann einer anderen Klasse übergeben, die diese Werte dann in eine Datenbank abspeichert. Mein Problem ist nun, dass diese ganze Sache viel zu lagen dauert. Ich würde bei so einer kleinen Datenmenge (150 Datensätze) eine Zeit von ca. 2-3 Sekunden erwarten, wenn überhaupt.
    Doch diese ganze Verarbeitung dauert im Schnitt 14 Sekunden!

    Kann mir einer einen Tip geben, mit dem ich diese Verarbeitung schneller geregelt bekomme?
    Es sollte trotzdem noch so sein, dass die ausgelesenen Werte einer anderen Klasse zur Speicherung in die DB übergeben werden.

    Vielen Dank für die Hilfe

    Gruß



  • Hallo

    da du nicht schreibst wie deine Verarbeitung im Detail (Codeauszug) aussieht, kann ich nur allgemein sagen : wahrscheinlich ist deine String-Verarbeitung, insbesondere die Aufsplittung anscheinden optimierungsbedürftig. Schau dir TStringList und die AnsiString-Methoden (nochmal) an.

    bis bald
    akari



  • Hallo akari, hier ist mal ein Codeausschnitt von mir:

    void c_ZErf2000_Protokoll::zerlegeAuftragsarbeitsdatensatz(AnsiString auftragsarbeitsdatensatz)
    {
    
       char *pWerteZeiger;
    
       pWerteZeiger = auftragsarbeitsdatensatz.c_str();
    
       //Den Kommentar überspringen
       while( *pWerteZeiger != ',' )
       {
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    //--[TAG]---------------------------------------------------------------------------------
    
       while( *pWerteZeiger != ',' )
       {
          m_asTag += *pWerteZeiger;
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    //--[AUFTRAGSNUMMER]----------------------------------------------------------------------
    
       while( *pWerteZeiger != ',' )
       {
          m_asAuftragsnummer += *pWerteZeiger;
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    //--[TAETIGKEITSNUMMER]-------------------------------------------------------------------
    
       while( *pWerteZeiger != ',' )
       {
          m_asTaetigkeitsnummer += *pWerteZeiger;
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    //--[ANFANG]------------------------------------------------------------------------------
    
       while( *pWerteZeiger != ',' )
       {
          m_asAnfang += *pWerteZeiger;
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    //--[ENDE]--------------------------------------------------------------------------------
    
       while( *pWerteZeiger != ',' )
       {
          m_asEnde += *pWerteZeiger;
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    //--[PAUSE]-------------------------------------------------------------------------------
    
       while( *pWerteZeiger != ',' )
       {
          m_asPause += *pWerteZeiger;
          pWerteZeiger++;
       }
       pWerteZeiger++;
    
    }
    

    Die eingelesenen Werte in den Membervariablen werden dann einer StringListe übergeben:

    pStringListe->Add(m_asTag);
    pStringListe->Add(m_asAuftragsnummer);
    pStringListe->Add(m_asTaetigkeitsnummer);
    pStringListe->Add(m_asAnfang);
    pStringListe->Add(m_asEnde);
    pStringListe->Add(m_asPause);
    

    Der nächste Schritt besteht darin, dass die StringListe der anderen Klasse übergeben wird. Auf der anderen Seite werden aus der StringListe die einzelnen Werte wieder den Membervariablen der Klasse zugewiesen und verarbeitet. Ich weiß, dass das zu viel Rumgespeichere mit den Variablen ist, doch macht sich das an der Performance bemerkbar?
    Was würdest du schätzen, wie lange es dauert 150 Datensätze aus einer Datei zu lesen? Von dieser Zeit gehen ja noch die Abspeicherungs- und Vergleichszeiten mit der Datenbank ab. Aber trotzdem ist es mir viel zu langsam. Es gibt Anwendungen, die mehrere tausend Datensätze innerhalb weniger Zeit in die DB speichern können.



  • Hallo

    benutzt statt diesen ganzen manuellen Zeichenvergleichen doch besser eine einzelne Funktion, die die AnsiString-Methode Pos einsetzt.

    bis bald
    akari



  • Ist es dann möglich nach dem "," zu suchen?

    Denn wenn ich das komma als Trennzeichen betrachte und mich danach orientiere, dann
    geht es doch nicht mit Pos oder?

    Wenn ich z.b. folgenden String habe

    Kommentar,34,Das ist ein Test,12:99,45,Noch ein test

    dann möchte ich alle einzelnen Werte in einer eigenen Variablen haben.
    und ich kenne ja nicht die genauen Bezeichnungen oder Werte nach denen ich suchen muss. Da bleibt mir ja nix anderes übrig als nach dem "," zu suchen.
    Man könnte den eingelesenen Teil ja vom restlichen String abschneiden, doch die Frage ist, ob die Pos-Funktion eine viel bessere Performance leistet als die Variante mit den Pointern!?



  • Schau dir doch mal in der Hilfe TStringList::CommaText an. Damit geht das ganz automatisch.



  • Hallo

    also nochmal meine Hinweise auf die AnsiString-Methoden Pos und SubString, nachzlesen auch in der BCB-Hilfe.

    /Edit : Okay seit dem BCB6 gibts ja noch bessere Möglichkeiten.

    bis bald
    akari



  • Bei solchen Sachen wende ich meisst StringReplace an und ersetze den Seperator durch einen Zeilenumbruch dann sind alle Einträge jeweils in einer eigenen Zeile.

    TStringList *strList = new TStringList();
    strList->Text = StringReplace(auftragsarbeitsdatensatz, ",", "\n", ReplaceFlags()<< rfReplaceAll);
    
    for (int anz=0; anz < strList->Count; anz++) {
        if (anz==0) //Kommentar
        if (anz==1) m_asTag = strList->Strings[anz]; //Tag
        if (anz==2) m_asAuftragsnummer = strList->Strings[anz]; //Auftragsnummer
        if (anz==3) m_asTaetigkeitsnummer = strList->Strings[anz]; //Taetigkeitsnummer
        if (anz==4) m_asAnfang = strList->Strings[anz]; //Anfang
        if (anz==5) m_asEnde = strList->Strings[anz]; //Ende
        if (anz==6) m_asPause = strList->Strings[anz]; //Pause
    }
    
    delete strList;
    


  • @akari
    CommaText gibt es auch schon im BCB5. Ab dem BCB6 ist dann noch DelimitedText sowie Delimiter hinzugekommen, die das Ganze noch flexibler machen.
    Es gibt aber noch viele andere Möglichkeiten. Sehr schön ist auch boost::tokenizer.



  • Hi Leute, hab jetzt mal die Variante von VergissEs genommen. Ist auf jeden Fall eleganter und kürzer!
    Doch zum Thema Performance hat sie auch nicht mehr beigetragen. Wenns hochkommt hab ich jetzt eine verbesserung von 1 Sekunde 😞

    Ich habe gehört, dass es eine Möglichkeit gibt, in der benutzen DB eine Importfunktion zu schreiben, die eine Datei einfach einliest und die Werte in die jeweiligen Tabellen schreibt und das alles in sehr sehr kurzer Zeit.

    Hat da jemand erfahrung damit. Und gebt mir mal eine Schätzung ab, wie lange ihr die Verarbeitungszeit von 150 Datensätzen sieht!?

    Gruß



  • Hast Du schon mal daran gedacht, einen Profiler einzusetzen? (z.B. AQtime)



  • Der Code von VergissEs ist schon etwas bearbeitungbedürftig.

    TStringList *strList = new TStringList;
    strList->CommaText = auftragsarbeitsdatensatz;
    if( strList->Count >= 6 )
    {
        m_asTag = strList->Strings[1]; //Tag
        m_asAuftragsnummer = strList->Strings[2]; //Auftragsnummer
        m_asTaetigkeitsnummer = strList->Strings[3]; //Taetigkeitsnummer
        m_asAnfang = strList->Strings[4]; //Anfang
        m_asEnde = strList->Strings[5]; //Ende
        m_asPause = strList->Strings[6]; //Pause
    } else
    {
    // Fehlerbehandlung
    }
    delete strList;
    


  • Braunstein schrieb:

    Der Code von VergissEs ist schon etwas bearbeitungbedürftig.

    nur wenn man daraus eine CommaText Version basteln will!



  • Nicht nur. Die for-Schleife ist auch unnötig.



  • Dann müsste ja deine if abfrage auch unnötig sein oder überprüfst du nicht auf vorkommen der Zeilen? 😮



  • Was glaubst, was diese if-Abfrage macht? Sie müsste korrekterweis aber so lauten:

    if( strList->Count > 6 )
    


  • Wenn du nochmal meinen Satz durch liesst wirst du festellen das ich geschrieben habe, das du durch dein if Abfrage die Anzahl überprüfst.

    Aber irgendwie drehen wir uns im Kreis, den je nach Anwendungsfall würde ich deine Lösung nehmen bzw. meine wenn ich Mehrfachparameter (also unterschiedliche Count habe aber trozdem eine feste Ordnung) hätte.

    Deshalb finde ich deine Aussage mein Codebeispiel sei bearbeitungsbedürftig für unsinnvoll, den es kommt immer drauf an was man daraus machen will und wie flexibel (meine Version) oder starr (deine Version) man es braucht/haben will.

    Diese Entscheidung muss der Thread Ersteller treffen nicht DU und auch nicht ICH

    In diesem Sinne



  • Um die String-Kopiererei ein wenig in den Griff zu bekommen, könntest du dir mal das "range"-Konzept von boost anschauen (http://www.boost.org/libs/range/index.html). Ich habe vor ein paar Monaten eine ähnliche Parsingroutine damit geschrieben, weils mir auch um Geschwindigkeit ging.
    Bei Gelegenheit werd ich die hier posten.

    Ansonsten kann ich mir nicht vorstellen, daß du für 150 Datensätze 15 Sekunden verlierst. Das ist ja "nix".
    Wie siehts denn mit deiner DB-Routine aus? Ich denke, daß hier viel Zeit draufgeht.


Log in to reply