2015-12-28 15 views
7

Parsuję plik dziennika o pojemności 500 GB, a moja wersja w C++ trwa 3,5 minuty, a wersji Go - 1,2 minuty.Poprawianie czytania pliku C++ wiersz po linii?

Używam strumieni C++ do streamowania każdej linii pliku do parsowania.

#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 ; 
} 

Po pierwsze, dlaczego tak wolno korzystać z tego kodu? Po drugie, jak mogę ją poprawić, aby była szybsza?

+2

* dlaczego tak wolno używać tego kodu * najpierw zmierzyć ponownie bez użycia std :: cout - teraz mierzysz plik I/O i drukujesz na konsolę – stijn

+6

'cout' jest z pętli. Nie powinno to mieć znaczenia. – Dialecticus

+2

Kiedy mówisz, że "parsujesz" plik, co przez to rozumiesz? Czy wszystko, co robisz, czyta i liczy linie? –

Odpowiedz

12

Standardowe biblioteki C++ iostreams są bardzo powolne i tak jest w przypadku wszystkich różnych implementacji standardowej biblioteki. Czemu? Ponieważ standard nakłada wiele wymagań na implementację, które hamują najlepszą wydajność. Ta część standardowej biblioteki została zaprojektowana mniej więcej 20 lat temu i nie jest zbyt konkurencyjna w testach wydajnościowych.

Jak można tego uniknąć? Korzystaj z innych bibliotek, aby uzyskać asynchroniczne operacje wejścia/wyjścia async takie jak asio lub natywne funkcje dostarczane przez twój system operacyjny.

Jeśli chcesz pozostać w standardzie, funkcja std::basic_istream::read() może spełnić Twoje wymagania dotyczące wydajności. Ale w tym przypadku musisz sam wykonać buforowanie i liczenie linii. Oto, jak można to zrobić.

#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 ; 
} 

Daj mi znać, jeśli jest szybciej!

4

Opierając się na @Ralph Tandetzky answer ale schodząc do niskopoziomowych funkcji C IO i zakładając platformę Linux przy użyciu systemu plików, który zapewnia dobrą bezpośrednie wsparcie IO (ale pobyt gwintowane pojedynczej):

#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); 
} 

Jeśli naprawdę musisz iść jeszcze szybciej, używać wielu wątków do czytania i liczenia nowych linii, abyś mógł czytać dane podczas liczenia nowych linii. Ale jeśli nie używasz naprawdę szybkiego sprzętu zaprojektowanego do wysokiej wydajności, jest to przesada.

0

Procedury we/wy starego dobrego C powinny być znacznie szybsze niż niezgrabne strumienie C++. Jeśli znasz rozsądne górne ograniczenie na długości wszystkich linii, możesz użyć fgets w połączeniu z buforem podobnym do char line[1<<20];. Ponieważ masz zamiar faktycznie analizować dane, możesz po prostu użyć fscanf bezpośrednio z pliku.

Należy pamiętać, że jeśli plik jest fizycznie przechowywany na dysku twardym, prędkość odczytu dysku twardego stanie się w każdym razie wąskim gardłem, zgodnie z informacją here. Dlatego tak naprawdę nie potrzebujesz najszybszego analizowania po stronie procesora, aby zminimalizować czas przetwarzania, być może wystarczy prosty kod.

Powiązane problemy