2015-12-28 40 views
7

Sto analizzando un file di registro ~ 500 GB e la mia versione C++ richiede 3,5 minuti e la versione Go richiede 1,2 minuti.Migliorare il file di lettura di C++ riga per riga?

Sto utilizzando gli stream di C++ per lo streaming di ogni riga del file in analisi.

#include <fstream> 
#include <string> 
#include <iostream> 

int main(int argc , char** argv) { 
    int linecount = 0 ; 
    std::string line ; 
    std::ifstream infile(argv[ 1 ]) ; 
    if (infile) { 
     while (getline(infile , line)) { 
      linecount++ ; 
     } 
     std::cout << linecount << ": " << line << '\n' ; 
    } 
    infile.close() ; 
    return 0 ; 
} 

In primo luogo, perché è così lento utilizzare questo codice? In secondo luogo, come posso migliorarlo per renderlo più veloce?

+2

* perché è così lento usare questo codice * prima misura di nuovo senza usare la parte std :: cout - stai misurando i file I/O e stampando su console – stijn

+6

'cout' è fuori dal ciclo. Non dovrebbe fare la differenza. – Dialecticus

+2

Quando dici che stai "analizzando" un file, cosa intendi con questo? Tutto quello che stai facendo è leggere e contare le linee? –

risposta

12

Le librerie standard C++ iostreams sono notoriamente lente e questo è il caso di tutte le diverse implementazioni della libreria standard. Perché? Perché lo standard impone molti requisiti all'implementazione che inibiscono le migliori prestazioni. Questa parte della libreria standard è stata progettata circa 20 anni fa e non è realmente competitiva su benchmark ad alte prestazioni.

Come puoi evitarlo? Utilizzare altre librerie per I/O asincrono ad alte prestazioni come boost asio o funzioni native fornite dal sistema operativo.

Se si desidera rimanere nello standard, la funzione std::basic_istream::read() potrebbe soddisfare le esigenze di prestazioni. Ma devi fare il buffering e il conteggio delle linee te stesso in questo caso. Ecco come può essere fatto.

#include <algorithm> 
#include <fstream> 
#include <iostream> 
#include <vector> 

int main(int, char** argv) { 
    int linecount = 1 ; 
    std::vector<char> buffer; 
    buffer.resize(1000000); // buffer of 1MB size 
    std::ifstream infile(argv[ 1 ]) ; 
    while (infile) 
    { 
     infile.read(buffer.data(), buffer.size()); 
     linecount += std::count(buffer.begin(), 
           buffer.begin() + infile.gcount(), '\n'); 
    } 
    std::cout << "linecount: " << linecount << '\n' ; 
    return 0 ; 
} 

Fammi sapere, se è più veloce!

4

Sulla @Ralph Tandetzky answer ma andando verso il basso per basso livello funzioni C IO, e assumendo una piattaforma Linux utilizzando un file system che fornisce un buon supporto diretto IO (ma rimanere single-threaded):

#define BUFSIZE (1024UL * 1024UL) 
int main(int argc, char **argv) 
{ 
    // use direct IO - the page cache only slows this down 
    int fd = ::open(argv[ 1 ], O_RDONLY | O_DIRECT); 

    // Direct IO needs page-aligned memory 
    char *buffer = (char *) ::valloc(BUFSIZE); 

    size_t newlines = 0UL; 

    // avoid any conditional checks in the loop - have to 
    // check the return value from read() anyway, so use that 
    // to break the loop explicitly 
    for (;;) 
    { 
     ssize_t bytes_read = ::read(fd, buffer, BUFSIZE); 
     if (bytes_read <= (ssize_t) 0L) 
     { 
      break; 
     } 

     // I'm guessing here that computing a boolean-style 
     // result and adding it without an if statement 
     // is faster - might be wrong. Try benchmarking 
     // both ways to be sure. 
     for (size_t ii = 0; ii < bytes_read; ii++) 
     { 
      newlines += (buffer[ ii ] == '\n'); 
     } 
    } 

    ::close(fd); 

    std::cout << "newlines: " << newlines << endl; 

    return(0); 
} 

Se davvero è necessario andare ancora più veloce, utilizzare più thread per leggere e contare i newline in modo da leggere i dati mentre si contano i newline. Ma se non stai usando hardware veramente veloce progettato per alte prestazioni, questo è eccessivo.

0

Le routine di I/O della vecchia C buona dovrebbero essere significativamente più veloci dei goffi flussi C++. Se si conosce il limite superiore ragionevole sulle lunghezze di tutte le linee, si può usare fgets accoppiato con un buffer come char line[1<<20];. Dal momento che stai per analizzare realmente i tuoi dati, potresti semplicemente utilizzare fscanf direttamente dal tuo file.

Si noti che se il file è fisicamente memorizzato su un disco rigido, quindi la velocità di lettura del disco rigido sarebbe comunque un collo di bottiglia, come indicato here. Ecco perché non hai davvero bisogno dell'analisi più veloce sul lato CPU per ridurre al minimo il tempo di elaborazione, forse sarebbe sufficiente il semplice fscanf.