Parsing Optimieren



  • Was verstehst du denn unter ineffizient? Wie schnell hättest du's gern, wie schnell läuft es jetzt ...



  • Willst du vielleicht, dass er es sozusagen im Hintergrund macht?



  • also der Ausschnitt is logischerweise ja nur ein sehr kleiner teil eines etwas größeren programms zur system - überwachung. Das Problem ist, dass er mir 50% der gesammten programmlaufzeit in dieser schleife verbring, sprich meine cpu - auslastung hängt immer so zwischen 5 und 8%, wenn das programm läuft.
    Laut profiler:
    % cumulative self self total
    time seconds seconds calls us/call us/call
    51,03 1,73 1,73 87141 19.85 19.85 Parsing

    und das bei einer ungefähren programmlaufzeit von 30min. Da dacht ich mir, dass müsste doch wesentlich schneller gehen, aber mir fällt keine andere Parsmethode ein.



  • @Norb,

    ich weiß nun nicht wie groß dein verwendeter
    Puffer ist. Aber es sieht so aus als würde
    er die Datei in mehreren Blöcken auslesen.

    Hast du schon mal daran gedacht die Datei
    auf einen Schlag zu puffern? Das würde die
    Festplattenzugriffe reduzieren.
    Und bei einem IDE System ist ein Festplattenzugriff
    auf gleichzeitig eine Prozessorbelastung.

    Zum Parsen hätte ich im Moment
    keine besseren Vorschläge.

    Bye Peter.



  • ja, hab auch schon mit fread versucht, und das ganze auf einmal eingelesen, hat aber keine große verbesserung gebracht.



  • @Norb,
    wie oft wird denn dieser Code
    in deinem Programm ausgeführt?

    Wenn das mehrere Male pro Sekunde
    geschieht kann man glaube ich nicht
    viel machen. Ist ja immerhin eine
    "ganz schöne" Arbeit für deine CPU.

    Das größte Problem was ich sehen ist, dass
    du sscanf mehrere Male auf den kompletten
    Puffer ausführst.

    Wenn ich heute Abend etwas Zeit habe versuche
    ich mal eine kleine Funktion zu schreiben.
    Wenn ich erfolgreich war poste ich den Code
    morgen mal.

    Bye Peter.



  • des wäre supernett, dankeschön!



  • @Norb,

    so das habe ich nun in meiner Mittagspause zusammengetippt.
    Ich habe es aber weder kompiliert und noch weniger getestet.
    Hoffe du kannst damit etwas anfangen.

    #define NAME_SZ 64
    #define DATA_SZ 128
    
    int main( void )
    {
      char *pBuffer = ...
      long lBufLen  = ...
      long lBegin   = 0;
      long lIndex   = 0;
      char cName[NAME_SZ];
      char cData[DATA_SZ];
      ...
      ... /* Data komplett puffern */
      ...  
      /* Ueber alle Dateieintraege */
      while ( parse( pBuffer, lBufLen, &lBegin, cName, cData ) )
      {
        /* Ich gehe jetzt mal davon aus das die 
           Datei immer gleich ausssieht. Sollte das 
           nicht der Fall sein, muss man via strcmp 
           den aktuellen Wert herausbekommen */
    
        switch ( lIndex )
        {
          case 0: strcmp( pidStat->comm,  cData ); continue;
          case 1: strcmp( pidStat->state, cData ); continue;
          ...
          ...
          ...
        };
    
        lIndex++;
      };
    
      return 0;
    };
    
    int parse( char *pBuf, long lBufLen, long *pBegin, 
               char *pRetName, char *pRetData )
    {
      long lGetPos;
      long lSetPos;
    
      if ( !pBuf || !pBegin )
        return 0;
    
      /* Naechsten Namen auslesen */
      while ( pBuf[lGetPos++] != ':' )
        cName[lSetPos++] = pBuf[lGetPos];
    
      /* Ueber ': ' springen */
      lGetPos += 2;
      /* lSetPos zuruecksetzen */
      lSetPos = 0;
    
      /* Naechsten Wert auslesen */
      while ( pBuf[lGetPos++] != '\r' )
        cData[lSetPos++] = pBuf[lGetPos];
    
      return 1;
    };
    

    Bye Peter.



  • Hab noch einige Fehler entdeckt.
    Kommt davon wenn man zu schnell etwas posten will 🙂

    #define NAME_SZ 64
    #define DATA_SZ 128
    
    int main( void )
    {
      char *pBuffer = ...
      long lBufLen  = ...
      long lBegin   = 0;
      long lIndex   = 0;
      char cName[NAME_SZ];
      char cData[DATA_SZ];
      ...
      ... /* Data komplett puffern */
      ...  
      /* Ueber alle Dateieintraege */
      while ( parse( pBuffer, lBufLen, &lBegin, cName, cData ) )
      {
        /* Ich gehe jetzt mal davon aus das die 
           Datei immer gleich ausssieht. Sollte das 
           nicht der Fall sein, muss man via strcmp 
           den aktuellen Wert herausbekommen */
    
        switch ( lIndex )
        {
          case 0: strcmp( pidStat->comm,  cData ); continue;
          case 1: strcmp( pidStat->state, cData ); continue;
          ...
          ...
          ...
        };
    
        lIndex++;
      };
    
      return 0;
    };
    
    int parse( char *pBuf, long lBufLen, long *pBegin, 
               char *pRetName, char *pRetData )
    {
      long lGetPos;
      long lSetPos;
    
      if ( !pBuf || !pBegin )
        return 0;
    
      /* Naechsten Namen auslesen */
      while ( pBuf[(*pBegin)+lGetPos++] != ':' )
        cName[lSetPos++] = pBuf[lGetPos];
    
      /* Ueber ': ' springen */
      lGetPos += 2;
      /* lSetPos zuruecksetzen */
      lSetPos = 0;
    
      /* Naechsten Wert auslesen */
      while ( pBuf[(*pBegin)+lGetPos++] != '\r' )
        cData[lSetPos++] = pBuf[lGetPos];
    
      /* CR/LF ueberspringen */
      lGetPos += 2;    
      /* Neue Begin position setzen */
      (*pBegin) += lGetPos;
    
      return 1;
    };
    

    Sollte aber grundsätzlich funktionieren.
    Falls es nicht klappt versuche ich es heute
    Abend wie versprochen nochmal.

    Bye Peter.



  • Wenn ich Dein Problem richtig verstanden habe, dann liegt der große Zeitverbrauch daran, daß Du, um eine Systemvariable zu füllen, jedesmal (fast) die ganze Konfigurationsdatei sequentiell durchkämmst. So korrekt?
    Daher folgender Vorschlag:
    - Speichere zu jeder Deiner Systemvariablen ihre Bezeichnung (char*), so wie sie in der Konfigurationsdatei auftauchen soll, und ihren Typ (z. B. string [char*] oder numerisch [unsigned, short, ...])
    - Bilde daraus ein sortiertes Array
    - Lies die Datei zeilenweise ein
    - Parse die Zeile in Variablenname und Wert
    - Versuche per binärer Suche, den Variablennamen im Array zu finden
    - Fülle die Variable gemäß ihrem Typ mit dem gelesenen Wert

    BOOL DateiLesen(const char* datei, PIDSTAT_TYPE pidStat)
    {
    	char zeile[ZEILE_SIZE];
    	char* varname;
    	char* wert;
    	FILE* file = fopen(datei, "r");
    	if(!file)
    		return FALSE;
    	while(fgets(zeile, ZEILE_SIZE, file))
    		if(ZeileParsen(zeile, &varname, &wert))
    			SystemVarFuellen(pidStat, varname, wert);
    	fclose(file);
    	return TRUE;
    }
    
    typedef enum _VarType
    {
    	VAR_CHAR,
    	VAR_UNSIGNED,
    	VAR_SHORT,
    	VAR_LONG,
    	VAR_STRING
    } VarType;
    
    typedef struct _VarInfo
    {
    	const char* name;
    	void* elem;
    	VarType type;
    } VarInfo;
    
    BOOL SystemVarFuellen(PIDSTAT_TYPE pidStat, char* varname, char* wert)
    {
    	static VarInfo vars[] =
    	{
    		{ "CapInh", &pidStat->inh,   VAR_STRING },
    		{ "CapPrm", &pidStat->prm,   VAR_STRING },
    		{ "Name",   &pidStat->comm,  VAR_STRING },
    		{ "State",  &pidStat->state, VAR_STRING },
    		{ "Uid",    &pidStat->Uid,   VAR_UNSIGNED },
    		{ "VmRSS",  &pidStat->rss,   VAR_UNSIGNED },
    		{ "VmSize", &pidStat->vsize, VAR_UNSIGNED },
    		/* etc. */
    	};
    	unsigned arrsize = sizeof(vars) / sizeof(VarInfo);
    	VarInfo* var = BinaereSuche(varname, vars, arrsize);
    	if(!var)
    		return FALSE;
    	switch(var->type)
    	{
    	case VAR_STRING:
    		strcpy(*((char**) var->elem), wert);
    		return TRUE;
    	case VAR_UNSIGNED:
    		*((unsigned*) var->elem) = (unsigned) atoi(wert);
    		return TRUE;
    	/* etc. */
    	}
    	return FALSE;
    }
    

Anmelden zum Antworten