2010-07-22 23 views
9

Cách thông thường để đọc một tập tin trong C++ là một trong những điều này:cách Fancy để đọc một tập tin trong C++: hiệu suất lạ vấn đề

std::ifstream file("file.txt", std::ios::binary | std::ios::ate); 
std::vector<char> data(file.tellg()); 
file.seekg(0, std::ios::beg); 
file.read(data.data(), data.size()); 

Đọc một tập tin 1.6 MB gần như ngay lập tức.

Nhưng gần đây, tôi đã phát hiện std::istream_iterator và muốn dùng thử để viết mã một chiều đẹp để đọc nội dung của tệp. Như thế này:

std::vector<char> data(std::istream_iterator<char>(std::ifstream("file.txt", std::ios::binary)), std::istream_iterator<char>()); 

Mã đẹp, nhưng rất chậm. Mất khoảng 2/3 giây để đọc cùng một tệp 1.6 MB. Tôi hiểu rằng đó có thể không phải là cách tốt nhất để đọc tệp nhưng tại sao lại là vì vậy chậm?

Đọc một tập tin theo một cách cổ điển đi như thế này (tôi đang nói chỉ về chức năng đọc):

  • các istream chứa một filebuf chứa một khối dữ liệu từ tập tin
  • sự gọi hàm số sgetn từ filebuf, sao chép từng ký tự một (không có memcpy) từ bộ đệm bên trong sang bộ đệm "dữ liệu"
  • khi dữ liệu bên trong của filebuf được đọc hoàn toàn, filebuf đọc khối tiếp theo từ tệp

Khi bạn đọc một tập tin sử dụng istream_iterator, nó đi như thế này:

  • vector gọi * iterator để có được những char tiếp theo (điều này chỉ đơn giản là đọc một biến), thêm nó đến cùng và tăng riêng của mình kích thước
  • nếu không gian được phân bổ của vector đầy (thường xảy ra không thường xuyên), việc di chuyển được thực hiện
  • thì nó sẽ gọi ++ iterator đọc char kế tiếp từ luồng (toán tử >> với tham số char chắc chắn chỉ cần gọi hàm sbumpc của filebuf)
  • cuối cùng nó so sánh trình lặp với trình lặp kết thúc, được thực hiện bằng cách so sánh hai con trỏ

Tôi phải thừa nhận rằng cách thứ hai không hiệu quả, nhưng ít nhất là 200 lần so với cách đầu tiên, làm sao có thể ?

Tôi nghĩ rằng kẻ giết người thực hiện là chuyển vị trí hoặc chèn, nhưng tôi đã thử tạo toàn bộ vectơ và gọi std :: copy và nó cũng chậm.

// also very slow: 
std::vector<char> data2(1730608); 
std::copy(std::istream_iterator<char>(std::ifstream("file.txt", std::ios::binary)), std::istream_iterator<char>(), data2.begin()); 
+0

"Tôi nghĩ rằng kẻ giết người hiệu suất là việc di chuyển hoặc chèn" - đây là lý do tại sao bạn cần phải dựa vào hồ sơ. –

Trả lời

6

Bạn nên so sánh táo với táo.

Mã đầu tiên của bạn đọc chưa định dạng dữ liệu nhị phân vì bạn sử dụng thành viên chức năng "đã đọc". Và không phải vì bạn sử dụng std :: ios_binary bằng cách này, hãy xem http://stdcxx.apache.org/doc/stdlibug/30-4.html để giải thích thêm, nhưng trong ngắn hạn: "Hiệu ứng của chế độ mở nhị phân thường bị hiểu lầm. Nó không đặt bộ chèn và bộ giải nén vào chế độ nhị phân, và do đó ngăn chặn các định dạng họ thường thực hiện. đầu vào và đầu ra nhị phân được thực hiện chỉ bằng basic_istream <> :: read() và basic_ostream <> :: write()"

Vì vậy, mã thứ hai của bạn với istream_iterator đọc định dạng văn bản. Đó là cách chậm hơn.

Nếu bạn muốn đọc dữ liệu nhị phân không định dạng, sử dụng istreambuf_iterator:

#include <fstream> 
#include <vector> 
#include <iterator> 

std::ifstream file("file.txt", std::ios::binary); 
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), 
          std::istreambuf_iterator<char>()); 

Trên nền tảng của tôi (VS2008), istream_iterator khoảng x100 chậm hơn so với read(). istreambuf_iterator hoạt động tốt hơn, nhưng vẫn chậm hơn x10 so với read().

+0

Cảm ơn, tôi không nghĩ về miền địa phương, chiều rộng để trích xuất, v.v. – Tomaka17

+0

Nhưng cách xử lý dữ liệu trong bộ nhớ chậm hơn 100 lần so với tệp I/O? – ruslik

1

Cách tiếp cận vòng lặp đọc từng tệp một lúc, trong khi tệp này chỉ đọc trong một lần truy cập.

Nếu hệ điều hành/trình xử lý tệp biết bạn muốn đọc một lượng lớn dữ liệu, có rất nhiều tối ưu có thể thực hiện - có thể đọc toàn bộ tệp trên một cuộc cách mạng của trục chính, không sao chép dữ liệu từ hệ điều hành bộ đệm cho bộ đệm ứng dụng.

Khi bạn thực hiện chuyển byte từng byte, hệ điều hành không có đầu mối nào bạn thực sự muốn làm, do đó không thể thực hiện tối ưu hóa như vậy.

+1

Đối tượng filebuf bên trong fstream đọc tệp theo từng khối. Dù sao cũng giống như cách để đọc tệp trong cả hai trường hợp, nó chỉ sao chép từ filebuf sang vector là chậm – Tomaka17

3

Chỉ hồ sơ sẽ cho bạn biết lý do chính xác. Đoán của tôi sẽ là những gì bạn đang thấy chỉ là chi phí của tất cả các cuộc gọi chức năng bổ sung liên quan đến phương pháp thứ hai. Thay vì một cuộc gọi duy nhất để mang lại tất cả dữ liệu, bạn đang thực hiện các cuộc gọi 1.6M * ... hoặc một thứ gì đó dọc theo các dòng đó.

* Nhiều người trong số họ là ảo nghĩa là hai chu kỳ CPU cho mỗi cuộc gọi. (Tks Zan)

+1

Vâng, và những cuộc gọi đó là gián tiếp ảo. Những người hút. –

+1

Hồ sơ cho tôi biết rằng rất nhiều chức năng tiêu thụ từ 3 đến 5% tổng số mỗi hàm, không có hàm nào xuất hiện ở trên cùng; Tôi đánh dấu bạn là câu trả lời được chấp nhận – Tomaka17

+0

@Zan Yeah! Điểm tốt, quên đề cập đến: ảo! – Gianni

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