2015-04-20 14 views
6

Così com'è, ci sono molti approcci per leggere un file in una stringa. due più comuni stanno usando ifstream :: Leggi per leggere direttamente in una stringa e utilizzando steambuf_iterators con std :: copy_n:Perché ifstream :: legge molto più velocemente rispetto all'utilizzo degli iteratori?

Utilizzando ifstream :: leggere:

std::ifstream in {"./filename.txt"}; 
std::string contents; 
in.seekg(0, in.end); 
contents.resize(in.tellg()); 
in.seekg(0, in.beg); 
in.read(&contents[0], contents.size()); 

Utilizzando std :: copy_n:

std::ifstream in {"./filename.txt"}; 
std::string contents; 
in.seekg(0, in.end); 
contents.resize(in.tellg()); 
in.seekg(0, in.beg); 
std::copy_n(std::streambuf_iterator<char>(in), 
      contents.size(), 
      contents.begin(); 

Molti benchmark mostrano che il primo approccio è molto più veloce rispetto alla seconda (nella mia macchina con g ++ - 4.9 è di circa 10 volte più veloce con entrambe le -O2 e -O3 bandiere) e mi chiedevo che cosa può essere il motivo di questa differenza di prestazioni.

+1

Sospetto che l'iteratore legge un carattere alla volta. Questo spiegherebbe la differenza. –

+0

@RSahu: in particolare, l'iteratore utilizza 1+ chiamate virtuali per ogni byte, mentre 'read' è 1-2 chiamate virtuali per buffer. –

+1

commento laterale: puoi anche costruire la stringa in posizione come 'std :: string contents (std :: streambuf_iterator (in), {});', non c'è bisogno di 'copy_n' e ottenere la dimensione del file. Ma probabilmente non farà una grande differenza in termini di velocità. – vsoftco

risposta

2

read è un singolo setup iostream (parte di ogni operazione iostream) e una singola chiamata al sistema operativo, che legge direttamente nel buffer fornito.

L'iteratore funziona estraendo ripetutamente un singolo char con operator>>. A causa della dimensione del buffer, questo potrebbe significare più chiamate al SO, ma soprattutto significa anche ripetute impostazioni e demolizioni della sentinella iostream, che potrebbe significare un blocco mutex e in genere significa un mucchio di altre cose. Inoltre, operator>> è un'operazione formattata, mentre read non è formattato, ovvero un sovraccarico di installazione aggiuntivo per ogni operazione.

Modifica: Gli occhi stanchi hanno visto istream_iterator invece di istreambuf_iterator. Ovviamente istreambuf_iterator non esegue input formattati. Si chiama sbumpc o qualcosa del genere sullo streambuf. Ancora un sacco di chiamate e l'utilizzo del buffer, che è probabilmente più piccolo dell'intero file.

+1

"L'iteratore funziona estraendo ripetutamente un singolo carattere con' operator >> '" - qualsiasi riferimento per quello? Infatti, 'operatore >>' ha un input formattato, mentre 'istreambuf_iterator' fa * input non formattato *, quindi penso che sia improbabile. (MODIFICA: Secondo [cppreference.com] (http://en.cppreference.com/w/cpp/iterator/istreambuf_iterator/operator*) questa risposta è errata.) –

+0

Questa è una sorta di definizione di cosa uno streambuf_iterator è, Konrad. Ogni volta che viene utilizzato, estrae un singolo carattere dal buffer del flusso corrispondente. – Peter

+0

@KonradRudolph Hai ragione. Ho letto male come istream_iterator. –