Gepufferte datei schreiben und buffer setzen



  • Hi,
    Ich würde gerne gepuffert in eine Datei schreiben. Leider wird mein buffer nicht verwendet. Woran kann das liegen??

    char *Buffer_ptr=new char [FILE_BUFFER_SIZE];
    std::filebuf *swap_file_ptr=::new std::filebuf();
    swap_file_ptr->open(TmpFullFileName, std::ios::in | std::ios::out | std::ios::trunc);
    
    swap_file_ptr->pubsetbuf(Buffer_ptr,FILE_BUFFER_SIZE);
    

    Die folgenden zeilen schreiben zwar in die geöffnete Datei, aber der Buffer_ptr bleibt immer leer.

    swap_file_ptr->sputn(tmp.c_str(),13);
    swap_file_ptr->pubsync();
    

    Wohiingegen diese zeilen nicht in die datei schreiben, jedoch natürlich der buffer nicht leer bleibt;

    strcpy(Buffer_ptr,tmp.c_str());
    swap_file_ptr->pubsync();
    

    😕 Woran kann das liegen 😕 ?? Was mache ich falsch ?? Das problem ist, daß ich genau den von mir vorgegebenen Buffer verwenden muß. Ansonsten gehts nicht. Also ich kann nicht zufrieden sein, daß die oberen Zeilen in die Datei schreiben. Es muß mit genau dem von mir angegebenen Buffer sein.
    Gruß


  • Mod

    Ich will jetzt nicht deinen Code durchgehen, denn deine Mühe ist umsonst: Die Dateizugriffe sind in der Regel schon gepuffert.



  • Wenn Du eine eigene Pufferung bauen willst, dann musst Du eine Scheribfunktion impleementieren, die zuerst in den Puffer schreibt:
    Im Prinzip so

    char buff[256];
    int buffered = 0;
    
    int Write (char *data, int len)
    {
       while (len)
       {
          // Anzahl bytes die in den Puffer sollen
          int bufferbytes = sizeof(buff)-buffered;
          if (len < bufferbytes)
             bufferbytes = len;
          // In den Puffer kopieren
          memcpy (buff+buffered, data, bufferbytes);
          // Anzahl bytes im Puffer merken
          buffered += bufferbytes;
          // Wenn der Puffer voll ist dann die Datei schreiben
          if (buffered == sizeof(buff))
          {
              fwrite (..., sizeof(buff));  // In Datei schreiben
              buffered = 0;
          }
          // Weitermachen bis alle Daten behandelt wurden
          data += bufferbytes;
          len -= bufferbytes; 
       }
    }
    

    Um den Puffer am Schluss zu leeren musst Du etwa sowas machen

    void Flush (void)
    {
       if (buffered)
       {
          fwrite (..., buffered);
          buffered = 0:
       }
    }
    

    Ohne Gewähr da ungetestet. 😉



  • Danke für Antwort.
    Das Problem ist, daß ich verhältnismäßig schnell schreiben und lesen Muß.
    Da 1Mio mal 1 Byte schreiben wesentlich länger dauert als 1 mal 1Mio byte, habe ich mich entschlossen einen buffer zum schreiben zu nehmen. Dabei muß ich die synchronisation (das flush() ) oder schreiben von Buffer auf Festplatte selbst bestimmmen können. Des Weitern wird nur dann geschrieben, wenn ich keinen Arbeitsspiecher mehr bekomme. Also muß auch der Buffer bereits alloiziiert sein.
    Die standard Bufferung bei datei I/O flusht jedesmal wenn in den Buffer geschrieben wird, unabhängig von den Daten im Buffer. Das ist für meine Verwendungszwecke nicht geeignet.
    Trotzdem ist der code schrot. habs jetzt hinbekommen. Ev. hat jemand das Selbe Problem. Daher hier die Lösung.

    std::fstream swap_file_ptr=::new std::fstream();
    char * Buffer_ptr=new char [FILE_BUFFER_SIZE];
    std::streambuf *Buffer_ptr_streambuf=swap_file_ptr->rdbuf()->pubsetbuf(Buffer_ptr,FILE_BUFFER_SIZE);
    swap_file_ptr->open(TmpFullFileName, std::ios::in | std::ios::out | std::ios::trunc);
    
    //In den Buffer schreiben. nach dem Schreiben kann man sich den Inhalt in 
    //Buffer_ptr anschauen. Es wird nicht in die datei geschrieben.
    //Die Funktion sputn merkt sich wieviele Zeichen man geschrieben hat
    Buffer_ptr_streambuf->sputn(tmp.c_str(),13);
    //Auch hier wird nicht in die datei geschrieben
    //Es wird an Buffer_ptr hinten angehängt
    Buffer_ptr_streambuf->sputn(tmp.c_str(),13);
    //Der inhalt von Buffer_ptr wird in die datei geschrieben.
    //Buffer_ptr enthält zwar noch Zeichen, diese werden aber beim 
    //nächsten Aufruf von sputn() überschrieben. Beim nächsten sync()
    //werden wieder nur die Zeichen in die Datei geschrieben, welche sputn()
    //seit dem letzten sync in Buffer_ptr geschrieben hat. 
    Buffer_ptr_streambuf->pubsync();
    

    Ist ne tolle Sache 😉
    👍
    Gruß



  • Danke general bacardi,
    Habs erst nach meiner Antwort gelesen. Deine Lösung schaut gut aus.
    Gruß



  • Nur interessehalber: Hast du mal nachgemessen, wie viel du mit deinem eigenen Buffer schneller bist als ohne? Mit welchem Compiler und OS?

    Stefan.



  • AlexXXx schrieb:

    Danke general bacardi,
    Habs erst nach meiner Antwort gelesen. Deine Lösung schaut gut aus.
    Gruß

    Ich habe es nicht getesttet aber das Prinzip ist klar. Wahrschinlich ist es performanter als deine std::streambuf Lösung, weil es sehr "direkt" ist.



  • Ja ich habe messungen gemacht. habe dabei eine bestimmte anzahl=N an bytes auf die Festplatte geschrieben. Das war zwar noch ungepuffert, aber ich habe
    1*N bytes geschrieben,
    und dan eben N in kleine Häppchen zerlegt und geschrieben.
    Ich habe die zahlen jetzt nicht mehr im kopf, aber schon beim Ausführen merkt man spürbar den zeitunterschied. Ansonsten hätte ich den standard buffer genommen.
    Des weiteren habe ich Die C I/O mit der C++ I/O (file I/O) verglichen.
    Bei großem N ist da kein unterschied mehrr meßbar. Auch wenn man N in häppchen zerlegt, war C nicht wirklich viel schneller.
    Das lohnt sich richtig, wenn man auf die Zeit achtenmuß.
    Gruß.
    P.s. Wenns wichtig ist, kann ich die Daten noch nachträglich schicken.



  • compiler habe ich GCC 4.3
    Aber mit:
    CFLAGS =-Wall -ansi -O0 -march=athlon-xp -g
    Mit -O2 ist -g schwachsinn, aber man kann noch was rausholen.
    Gruß



  • OS ist ein Linux,
    Habich ganz vergessen. Zu Windows kann ich da nichts sagen momentan.
    Würde mich aber trotzdem freuen wenns auch da schneller wäre.
    Also wenn jemand was weiß.
    Gruß



  • Hallo Alex,

    wenn du wirklich schnell eine große Anzahl von Daten lesen und scheiben willst, dann würde ich stattdessen den ungepufferten Zugriff wählen (denn durch den Puffer hast du unnötige Kopierarbeiten):

    swap_file_ptr->setbuf(0, 0);
    

    Teste es mal aus...


Anmelden zum Antworten