2015-04-17 31 views
5

Tôi biết rất nhiều tối ưu hóa của trình biên dịch có thể khá bí truyền, nhưng ví dụ của tôi rất đơn giản, tôi muốn xem liệu tôi có thể hiểu được không, nếu có ai có ý tưởng về việc nó có thể làm gì.Trình biên dịch tối ưu hóa getline() hiệu quả như thế nào?

Tôi có tệp văn bản 500 mb. Tôi tuyên bố và khởi tạo một fstream:

std::fstream file(path,std::ios::in) 

Tôi cần phải đọc tệp tuần tự. Đó là tab phân tách nhưng độ dài trường không được biết và thay đổi dòng. Việc phân tích cú pháp thực tế tôi cần phải làm cho mỗi dòng thêm rất ít thời gian vào tổng số (điều đó thực sự làm tôi ngạc nhiên vì tôi đang thực hiện chuỗi :: tìm trên mỗi dòng từ đường thẳng. Tôi nghĩ điều đó sẽ chậm).

Nói chung, tôi muốn tìm kiếm từng dòng cho một chuỗi và hủy bỏ vòng lặp khi tìm thấy. Tôi cũng có nó tăng lên và phun ra các số dòng cho sự tò mò của riêng tôi, tôi xác nhận điều này cho biết thêm ít thời gian (5 giây hoặc lâu hơn) và cho phép tôi xem cách nó thổi qua các đường ngắn và làm chậm trên các dòng dài.

Tôi có văn bản được tìm thấy là chuỗi duy nhất gắn thẻ eOF, vì vậy cần tìm kiếm mọi dòng. Tôi đang làm điều này trên điện thoại của tôi vì vậy tôi xin lỗi vì vấn đề định dạng nhưng nó khá đơn giản. Tôi có một chức năng lấy fstream của tôi như là một tài liệu tham khảo và các văn bản được tìm thấy như là một chuỗi và trở về một std :: size_t.

long long int lineNum = 0; 
while (std::getline (file, line)) 
{ 
    pos = line.find(text); 
    lineNum += 1; 
    std::cout << std::to_string(lineNum) << std::endl; 
    if (pos != -1) 
     return file.tellg(): 
} 
    return std::string::npos; 

Chỉnh sửa: lingxi chỉ ra to_string không cần thiết ở đây, cảm ơn. Như đã đề cập, hoàn toàn bỏ qua tính toán số lượng đầu ra và đầu ra tiết kiệm một vài giây, trong ví dụ trước khi được tối ưu hóa của tôi là một phần nhỏ trong tổng số.

Điều này chạy thành công qua mọi dòng và trả về vị trí cuối trong 408 giây. Tôi đã có cải thiện tối thiểu cố gắng để đưa các tập tin trong một chuỗi, hoặc bỏ qua tất cả mọi thứ trong toàn bộ vòng lặp (chỉ getline cho đến khi kết thúc, không kiểm tra, tìm kiếm, hoặc hiển thị). Cũng đặt trước một không gian rất lớn cho chuỗi đã không giúp đỡ.

Dường như tuyến đường hoàn toàn là trình điều khiển. Tuy nhiên ... nếu tôi biên dịch với cờ/O2 (MSVC++), tôi sẽ nhanh hơn 26 giây. Ngoài ra, không có sự chậm trễ rõ ràng trên các đường dài so với ngắn. Rõ ràng trình biên dịch đang làm một cái gì đó rất khác nhau. Không có khiếu nại từ tôi, nhưng bất kỳ suy nghĩ như thế nào nó đạt được? Như một bài tập tôi muốn thử và lấy mã của mình để thực thi nhanh hơn trước khi tối ưu hóa trình biên dịch.

Tôi đặt cược nó có một cái gì đó để làm với cách getline thao tác chuỗi. Nó sẽ nhanh hơn (alas không thể kiểm tra một lúc) để chỉ dành toàn bộ kích thước cho chuỗi, và đọc ký tự theo ký tự, tăng số dòng của tôi khi tôi vượt qua một/n? Ngoài ra, trình biên dịch sẽ sử dụng những thứ như mmap?

CẬP NHẬT: Tôi sẽ đăng mã khi tôi về nhà tối nay. Có vẻ như việc tắt kiểm tra thời gian chạy đã làm giảm thời gian thực hiện từ 400 giây xuống còn 50! Tôi đã thử thực hiện các chức năng tương tự bằng cách sử dụng mảng c kiểu thô. Tôi không phải là siêu kinh nghiệm, nhưng nó đã được dễ dàng, đủ để đổ dữ liệu vào một mảng ký tự, và vòng qua nó tìm kiếm newlines hoặc chữ cái đầu tiên của chuỗi mục tiêu của tôi.

Ngay cả ở chế độ gỡ lỗi đầy đủ, nó sẽ kết thúc và tìm chuỗi chính xác trong 54 giây. 26 giây với séc tắt và tối đa 20 giây. Vì vậy, từ các thử nghiệm không chính thức, ad-hoc của tôi, có vẻ như các hàm chuỗi và luồng được nạn nhân bởi kiểm tra thời gian chạy? Một lần nữa, tôi sẽ kiểm tra lại khi tôi về nhà.

+0

C++ nhanh, yo. – Barry

+0

Bạn có thể chỉ cần 'std :: cout << lineNum << std :: endl;' có thể giúp bạn tiết kiệm thời gian. – Lingxi

+1

Đây phải là lần đầu tiên tôi thấy ai đó gọi iostreams "nhanh". Khả năng bao gồm bỏ qua kiểm tra gỡ lỗi (thư viện chuẩn của MSVC có rất nhiều trong số đó), tốt hơn nội tuyến, devirtualization. –

Trả lời

1

Lý do cho sự tăng tốc đáng kể này là phân cấp lớp iostream dựa trên mẫu (std::ostream thực sự là kiểu chữ của mẫu có tên là std::basic_ostream) và rất nhiều mã của nó nằm trong tiêu đề. C++ iostreams thực hiện một số cuộc gọi hàm để xử lý từng byte trong luồng. Tuy nhiên, hầu hết các chức năng này đều khá tầm thường. Bằng cách bật tối ưu hóa, hầu hết các cuộc gọi này được trình bày, hiển thị cho trình biên dịch thực tế là std::getline về cơ bản sao chép các ký tự từ bộ đệm này sang bộ đệm khác cho đến khi nó tìm thấy dòng mới - thông thường đây là "ẩn" trong nhiều lớp gọi hàm. Điều này có thể được tối ưu hóa hơn nữa, giảm chi phí cho mỗi byte theo thứ tự độ lớn.

Hành vi đệm thực sự không thay đổi giữa phiên bản được tối ưu hóa và không được tối ưu hóa, nếu không tốc độ sẽ cao hơn nữa.

+0

Trình biên dịch LTO như MSVC cũng có thể xem toàn bộ chương trình và thay đổi các cuộc gọi hàm ảo thành các cuộc gọi trực tiếp. Điều đó giúp iostreams rất nhiều. –

+0

"lý do cho sự tăng tốc đáng kể này" - tăng tốc đáng kể? Q mô tả sự tăng tốc từ tối ưu hóa là "hài hước". Việc tối ưu hóa thực sự quan trọng đối với hiệu suất của iostream * nếu I/O có thể được thực hiện đủ nhanh để nhấn mạnh mã *, nhưng đó là vì số lần đọc đĩa thực tế được thực hiện trong cùng một khối đệm mà hiệu suất được quan sát tương đối nhất quán cho OP. –

+0

Nếu bạn đọc số OP, sử dụng/O2 sẽ giảm thời gian hoạt động xuống 20. Yếu tố khá rõ ràng so với phần còn lại của bài viết mà OP không mong đợi một tốc độ lớn như vậy, và "nhanh hơn" thực sự là cách kỳ lạ của anh nói "nhanh hơn đáng kể". –

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