2015-12-28 14 views
7

Tôi đang phân tích cú pháp tệp nhật ký ~ 500GB và phiên bản C++ mất 3,5 phút và phiên bản Go của tôi mất 1,2 phút.Cải thiện dòng tệp đọc của C++ theo dòng?

Tôi đang sử dụng luồng của C++ để truyền mỗi dòng của tệp để phân tích cú pháp.

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

Thứ nhất, tại sao quá chậm để sử dụng mã này? Thứ hai, làm thế nào tôi có thể cải thiện nó để làm cho nó nhanh hơn?

+2

* tại sao nó quá chậm để sử dụng mã này * đầu tiên đo một lần nữa mà không sử dụng phần :: cout std - bây giờ bạn đang đo tệp I/O và in để bàn điều khiển – stijn

+6

'cout' nằm ngoài vòng lặp. Không nên tạo sự khác biệt. – Dialecticus

+2

Khi bạn nói rằng bạn đang "phân tích cú pháp" một tệp, điều đó có nghĩa là gì? Có phải tất cả các bạn đang đọc và đếm các dòng? –

Trả lời

12

Thư viện chuẩn C++ iostreams nổi tiếng chậm và đây là trường hợp cho tất cả các triển khai khác nhau của thư viện chuẩn. Tại sao? Bởi vì tiêu chuẩn áp đặt rất nhiều yêu cầu về việc thực hiện mà ngăn cản hiệu suất tốt nhất. Phần này của thư viện chuẩn được thiết kế khoảng 20 năm trước và không thực sự cạnh tranh trên các tiêu chuẩn hiệu suất cao.

Làm cách nào bạn có thể tránh nó? Sử dụng các thư viện khác cho hiệu năng cao I/O async như tăng cường asio hoặc các chức năng bản địa được cung cấp bởi hệ điều hành của bạn.

Nếu bạn muốn ở trong tiêu chuẩn, chức năng std::basic_istream::read() có thể đáp ứng nhu cầu hiệu suất của bạn. Nhưng bạn phải làm bộ đệm của bạn và dòng đếm mình trong trường hợp này. Đây là cách nó có thể được thực hiện.

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

Hãy cho tôi biết, nếu nó nhanh hơn!

4

xây dựng trên @Ralph Tandetzky answer nhưng đi xuống đến mức thấp chức năng C IO, và giả sử một nền tảng Linux sử dụng một hệ thống tập tin mà cung cấp hỗ trợ IO trực tiếp tốt (nhưng ở đơn luồng):

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

Nếu bạn thực sự cần phải đi nhanh hơn, sử dụng nhiều chủ đề để đọc và đếm các dòng mới để bạn đọc dữ liệu trong khi đang đếm các dòng mới. Nhưng nếu bạn không chạy trên phần cứng thực sự nhanh được thiết kế cho hiệu suất cao, điều này là quá mức cần thiết.

0

Các thủ tục I/O từ C cũ tốt sẽ nhanh hơn đáng kể so với các luồng C++ vụng về. Nếu bạn biết giới hạn trên hợp lý trên độ dài của tất cả các dòng, bạn có thể sử dụng fgets cùng với bộ đệm như char line[1<<20];. Vì bạn sẽ thực sự phân tích cú pháp dữ liệu, bạn có thể chỉ cần sử dụng trực tiếp fscanf từ tệp của mình.

Lưu ý rằng nếu tệp của bạn được lưu trữ trên ổ cứng, thì tốc độ đọc ổ cứng sẽ trở thành nút cổ chai, như được ghi chú here. Đó là lý do tại sao bạn không thực sự cần phân tích cú pháp phía CPU nhanh nhất để giảm thiểu thời gian xử lý, có lẽ đơn giản là fscanf sẽ đủ.

Các vấn đề liên quan