2012-05-04 16 views
22

Di seguito sono riportati due programmi che scrivono 50.000.000 byte in un file.La scrittura di file C++ ofstream utilizza un buffer?

Il primo programma, scritto in C, utilizza un buffer, che una volta riempito con un valore arbitrario, scrive su disco e quindi ripete quel processo finché non vengono scritti tutti i 50.000.000 byte. Ho notato che mentre aumentavo la dimensione del buffer, il programma richiedeva meno tempo per l'esecuzione. Ad esempio, a BUFFER_SIZE = 1, il programma ha richiesto ~ 88,043 secondi, mentre a BUFFER_SIZE = 1024, il programma ha impiegato solo 1,77773 secondi. Il miglior tempo che ho registrato è stato quando BUFFER_SIZE = 131072. Dato che il BUFFER_SIZE è aumentato più in alto di così, ho notato che ha iniziato a richiedere un po 'più di tempo.

Il secondo programma, scritto in C++, utilizza ofstream per scrivere un byte alla volta. Con mia sorpresa, il programma ha impiegato solo 1,87 secondi per funzionare. Mi aspettavo che ci volesse circa un minuto, come il programma C con BUFFER_SIZE = 1. Ovviamente, il C++ ofstream gestisce la scrittura dei file in modo diverso da come pensavo. Secondo i miei dati, sta funzionando in modo abbastanza simile al file C con BUFFER_SIZE = 512. Utilizza una sorta di buffer dietro le quinte?

Ecco il programma C:

const int NVALUES = 50000000; //#values written to the file 
const char FILENAME[] = "/tmp/myfile"; 
const int BUFFER_SIZE = 8192; //# bytes to fill in buffer before writing 

main() 
{ 
    int fd; //File descriptor associated with output file 
    int i; 
    char writeval = '\0'; 
    char buffer[BUFFER_SIZE]; 

    //Open file for writing and associate it with the file descriptor 
    //Create file if it does not exist; if it does exist truncate its size to 0 
    fd = open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); 

    for(i=0;i<NVALUES;i++) 
    { 
     //Package bytes into BUFFER_SIZE chunks 
       //and write a chunk once it is filled 
     buffer[i%BUFFER_SIZE] = writeval; 
     if((i%BUFFER_SIZE == BUFFER_SIZE-1 || i == NVALUES-1)) 
      write(fd, buffer, i%BUFFER_SIZE+1); 

    } 

    fsync(fd); 

    close(fd); 
} 

Ecco il programma C++:

int main() 
{ 
    ofstream ofs("/tmp/iofile2"); 
    int i; 

    for(i=0; i<50000000; i++) 
     ofs << '\0'; 

    ofs.flush(); 
    ofs.close(); 

    return 0; 
} 

Grazie per il vostro tempo.

+4

Sì, è tamponato dietro le quinte, come "ofs.flush();" indica. –

+7

Oh! Mi piace molto quando i "nuovi arrivati" fanno questo tipo di domande: dati sperimentali, ipotesi intelligenti, ... Signore, è un piacere averti a bordo! –

+0

Ho sempre sentito 4096 (dimensioni della pagina del sistema operativo) o 8192 (dimensioni della pagina del doppio sistema operativo) erano il numero migliore a cui puntare. Dimensioni più grandi di quelle potrebbero essere più veloci, ma l'aumento di velocità non valeva l'aumento della memoria. –

risposta

8

Sì, gli ostreams utilizzano un buffer di flusso, una sottoclasse di un'istanza del modello basic_streambuf. L'interfaccia di basic_streambuf è progettata in modo che un'implementazione possa eseguire il buffering se c'è un vantaggio in questo.

Tuttavia, questo è un problema di qualità di implementazione. Le implementazioni non sono obbligate a farlo, ma qualsiasi implementazione competente lo farà.

Potete leggere tutto su di esso nel capitolo 27 della norma ISO, anche se forse una fonte più leggibile è La libreria standard C++: Un tutorial e una Riferimento (google search).

2

Per this, ofstream ha un puntatore interno filebuf, possono essere letti attraverso la funzione rdbuf, che punta a un oggetto streambuf, che è questo:

streambuf oggetti sono solitamente associati a uno specifico carattere sequenza , dal quale leggono e scrivono i dati tramite un buffer di memoria interno . Il buffer è una matrice in memoria che è prevista per essere sincronizzata quando necessario con il contenuto fisico della sequenza di caratteri associata .

Ho messo in evidenza i bit importanti, sembra che faccia uso di un buffer, ma non so o non ho scoperto che tipo di buffer è.

+0

Grazie per quello, devo averlo perso mentre stavo visualizzando la pagina di riferimento C++. – TimeBomb006

+1

@ TimeBomb006: Non è la pagina di riferimento C++. È una pagina di riferimento * a * C++, e se stai cercando una risposta definitiva, non è affidabile. –

+0

Grazie @BenjaminLindley per questo chiarimento. – TimeBomb006

9

Sì, tutte le operazioni di streaming sono bufferizzate, sebbene per impostazione predefinita l'input standard, l'output e l'output di errore non siano tali da rendere meno sorprendenti le interazioni con il C IO.

Come già accennato, esiste una classe di base streambuf utilizzata dietro le quinte. È dotato di un proprio buffer, la cui dimensione è un dettaglio di implementazione.

È possibile controllare (sperimentalmente) la quantità di questo buffer è quello di utilizzare streambuf::in_avail, assumendo che filestream di ingresso e di uscita sono filestream di installazione con la stessa dimensione del buffer ...

Ci sono altre due operazioni che si possono fare qui che potrebbe essere di interesse:

  • è possibile modificare l'oggetto streambuf utilizzata da un ruscello, per passare ad una versione personalizzata
  • è possibile modificare il buffer utilizzato dall'oggetto streambuf

sia dovrebbe essere fatto sia a destra dopo la creazione del flusso o dopo una flush, per timore che alcuni dati vengono persi ...

per illustrare il cambiamento di buffer, controlla streambuf::putsetbuf:

#include <fstream> 
#include <vector> 

int main() { 
    std::vector<char> vec(512); 

    std::fstream fs; 
    fs.rdbuf()->pubsetbuf(&vec.front(), vec.size()); 

    // operations with file stream here. 
    fs << "Hello, World!\n"; 

    // the stream is automatically closed when the scope ends, so fs.close() is optional 
    // the stream is automatically flushed when it is closed, so fs.flush() is optional 

    return 0; 
} 

Ora puoi ripetere gli esperimenti che hai fatto in C per trovare il punto debole :)

+0

Non è vero che tutte le operazioni di streaming sono memorizzate nel buffer - https://connect.microsoft.com/VisualStudio/feedback/details/642876/std-wcout-is-ten-times-slower-than-wprintf-performance-bug- in-c-library – Sam

+0

@Sam: Suppongo che tu ti stia riferendo a * Il problema è che quando si stampa sulla console (invece di essere reindirizzati ad un file), né il C né il C++ I/O vengono bufferati di default . * (altrimenti, per favore preciso). Indicherei che la mia prima frase termina con * anche se per impostazione predefinita l'input standard, l'output e l'output dell'errore non sono così che le interazioni con il C IO sono meno sorprendenti * che è esattamente la stessa cosa. –