2012-02-14 57 views
14

Ai đó có thể giải thích khái niệm về bộ đệm một cách rõ ràng hơn một chút không? Tôi hiểu rằng bộ đệm là cấu trúc dữ liệu nơi các ký tự được lưu trữ và nơi dữ liệu được đọc từ đó. Ý tưởng của bộ đệm xả là gì?Bộ đệm C++ và cout, và bộ đệm nói chung

Khi bộ đệm bị xóa, đây có phải là hành vi viết các ký tự được lưu trong đó không?

Từ văn bản:

To avoid the overhead of writing in response to each output request, the library uses the 
buffer to accumulate the characters to be written, and flushes the buffer, by writing its 
contents to the output device, only when necessary. By doing so, it can combine several 
output operations into a single write. 

Khi đề cập đến 'xả' mà gần như làm cho nó âm thanh như thể bộ đệm đang được viết mà còn xóa cùng một lúc. Chỉ cần đầu cơ.

Vì vậy, để được viết để xem trên màn hình, yêu cầu xóa bộ đệm là gì?

When our program writes its prompt to cout, that output goes into the buffer associated 
with the standard output stream. Next, we attempt to read from cin. This read flushes 
the cout buffer, so we are assured that our user will see the prompt. 

Ở đây, có vẻ như bằng cách sử dụng 'endl' ở cuối nó cho biết hệ thống cần ghi ngay lập tức (ngụ ý là không?) Endl không được sử dụng là gì?

Writing the value of std::endl ends the line of 
output, and then flushes the buffer, which forces the system to write to the output 
stream immediately. 
+0

câu hỏi này đã được hỏi và trả lời rất nhiều thời gian trong SO. http://stackoverflow.com/a/4752069/1155650 –

Trả lời

2

nghĩ về những gì sẽ xảy ra nếu, mỗi khi bạn "viết" một byte vào một tập tin đĩa, chương trình của bạn thực sự đi ra ngoài đĩa, đọc trong lĩnh vực/cụm/khối hiện tại, thay đổi các byte đơn sau đó viết nó trở lại đĩa vật lý.

Tôi muốn nói phần mềm của bạn sẽ được mô tả tốt nhất như "băng" về hiệu suất :-)

Đệm là một phương tiện để trộn lên đọc và viết để làm cho họ hiệu quả hơn.

Ví dụ: nếu bạn đang ghi vào tệp đĩa, nó có thể đợi cho đến khi bạn có khối 4K đầy đủ trước khi ghi vào đĩa.

Khi đọc, nó có thể nhận được khối 4K mặc dù bạn chỉ yêu cầu mười byte, với giả định rằng bạn có thể yêu cầu phần còn lại ngay.

Việc xóa bằng văn bản diễn ra hoàn toàn khi bộ nhớ cache đầy hoặc rõ ràng nếu được yêu cầu (với cuộc gọi xả hoặc nếu bạn đóng tệp). Lưu ý rằng tệp đệm này chỉ là loại đệm, khái niệm này có thể được sử dụng ở bất kỳ nơi nào mà lợi ích hiệu quả có thể được thực hiện bằng cách "chunking" lên đọc và viết.

21

Ý tưởng cơ bản về đệm là kết hợp các thao tác thành các khối lớn hơn: thay vì đọc số lượng byte nhỏ, đọc toàn bộ trang và làm cho sẵn theo yêu cầu; thay vì viết số lượng nhỏ các byte, hãy đệm chúng lên và viết toàn bộ trang hoặc khi viết được yêu cầu một cách rõ ràng. Về cơ bản, đây là một tối ưu hóa hiệu suất quan trọng. Nó khá rõ ràng cắt cho các hoạt động I/O nhưng thường áp dụng cho các ứng dụng khác: xử lý nhiều đơn vị cùng một lúc thường kết thúc để được nhanh hơn so với chế biến các đơn vị cá nhân.

Đối với I/O flushing đề cập đến việc ghi byte hiện đang lưu vào đích - bất kể điều này có nghĩa là trong thực tế. Đối với C++ IOStreams xả một lượng dòng để gọi hàm thành viên std::ostream::flush() mà lần lượt gọi std::streambuf::pubsync() trên bộ đệm luồng được liên kết (điều này bỏ qua chi tiết rằng luồng thực sự là các mẫu lớp, ví dụ:std::basic_ostream<cT, traits>; cho mục đích của cuộc thảo luận này không quan trọng là chúng là các mẫu lớp): lớp cơ sở std::streambuf là sự trừu tượng của C++ về cách một luồng nhất định được xử lý. Nó khái niệm bao gồm một đầu vào và một bộ đệm đầu ra cộng với chức năng ảo chịu trách nhiệm đọc hoặc viết các bộ đệm tương ứng. Hàm std::streambuf::pubsync() gọi hàm ảo std::streambuf::sync() cần được ghi đè cho mọi bộ đệm luồng có khả năng đệm các ký tự. Tức là, việc thực hiện flushing thực sự có nghĩa là phụ thuộc vào cách thực hiện chức năng ảo này.

Cho dù ghi đè sync() thực sự làm điều gì đó và điều gì phụ thuộc rõ ràng vào bộ đệm dòng đại diện. Ví dụ, đối với một số std::filebuf chịu trách nhiệm đọc hoặc ghi vào một tệp, sync() ghi nội dung hiện tại của bộ đệm và loại bỏ các ký tự bị xóa khỏi bộ đệm. Do một tệp có thể không thực sự đại diện cho một tệp thực nhưng ví dụ: một ống được đặt tên để giao tiếp với một quy trình khác, đây là hành vi hợp lý. Mặt khác, xả một std::stringbuf là bộ đệm luồng được sử dụng để thực hiện ghi vào một số std::string được sử dụng, ví dụ: bởi std::ostringstream thực sự không làm bất cứ điều gì: chuỗi là hoàn toàn trong chương trình và std::string đại diện cho giá trị của nó được xây dựng khi hàm thành viên std::stringbuf::str() được gọi. Đối với bộ đệm luồng do người dùng xác định có nhiều hành vi khác nhau. Lớp đệm luồng phổ biến là lọc đầu ra bằng cách nào đó trước khi chuyển nó vào bộ đệm luồng khác (ví dụ: bộ đệm ghi nhật ký có thể thêm dấu thời gian sau mỗi dòng mới). Chúng thường chỉ gọi hàm std::streambuf::pubsync() của bộ đệm luồng tiếp theo.

Do đó, mô tả về hành vi thực tế thường được giữ khá mơ hồ bởi vì nó không thực sự rõ ràng chính xác những gì sẽ xảy ra. Về mặt khái niệm, xả một luồng hoặc gọi pubsync() trên bộ đệm luồng nên cập nhật điểm đến bên ngoài của ký tự cho phù hợp với trạng thái nội bộ hiện tại. Nói chung, số tiền này để chuyển tiếp các ký tự đệm hiện tại và loại bỏ chúng khỏi bộ đệm bên trong. Tại thời điểm này nó là đáng chú ý là bộ đệm thường cũng được viết khi nó chỉ là đầy đủ. Khi nó được đầy đủ phụ thuộc, một lần nữa, trên bộ đệm dòng cụ thể. Việc thực hiện tốt std::filebuf về cơ bản sẽ điền vào một bộ đệm byte phù hợp với kích thước của trang cơ bản (hoặc bội số của nó) và sau đó viết các trang hoàn chỉnh, giảm thiểu số lượng các hoạt động I/O cần thiết (điều này thực sự tương đối khó thực hiện vì bộ đệm kích thước khác nhau giữa các hệ thống tệp khác nhau và tùy thuộc vào mã hóa được sử dụng khi viết số byte sản xuất không thể ước tính dễ dàng).

C++ thư viện chuẩn thường không đòi hỏi bừng rõ ràng:

  • Dòng std::cerr được thiết lập để tự động tuôn ra bất kỳ sản xuất sau mỗi lần hoạt động đầu ra được gọi. Đây là kết quả của cờ định dạng std::ios_base::unitbuf được đặt theo mặc định. Để tắt tính năng này, bạn có thể sử dụng std::cerr << std::nounitbuf hoặc chỉ có thể sử dụng std::clog ghi cùng một đích nhưng không thực hiện thao tác xả này.
  • Khi đọc từ std::istream "được gắn" std::ostream, nếu có, bị xóa. Theo mặc định, std::cout được gắn với std::cin. Nếu bạn muốn tự mình thiết lập một số điện thoại std::ostream, bạn có thể sử dụng ví dụ: in.tie(&out) hoặc, nếu bạn muốn loại bỏ một ràng buộc std::ostream bạn có thể sử dụng ví dụ: std::cin.tie(0).
  • Khi std::ostream bị hủy (hoặc khi nó thường bị phá hủy trong trường hợp std::ostream là một trong các luồng chuẩn), thì std::ostream bị xóa.
  • Khi bộ đệm của luồng sẽ tràn chức năng ảo std::streambuf::overflow() được gọi là thường ghi bộ đệm của bộ đệm hiện tại (cộng với ký tự được chuyển, nếu có) đến đích của nó. Điều này thường được thực hiện bằng cách chỉ cần gọi sync() để xóa bộ đệm hiện tại nhưng những gì được thực hiện chính xác, một lần nữa, phụ thuộc vào bộ đệm luồng bê tông.

Bạn cũng có thể yêu cầu những rõ ràng đỏ bừng một std::ostream:

  • Bạn có thể gọi các hàm thành viên std::ostream::flush(), ví dụ std::cout.flush().
  • Bạn có thể gọi hàm thành viên std::streambuf::pubsync(), ví dụ: std::cout.rdbuf()->pubsync() (giả sử có một bộ đệm luồng được thiết lập; gọi std::ostream::flush() sẽ không hoạt động nếu không có bộ đệm luồng).
  • Bạn có thể sử dụng trình điều khiển std::flush, ví dụ: std::cout << std::flush.
  • Bạn có thể sử dụng trình xử lý std::endl, ví dụ: std::cout << std::endl để trước tiên viết ký tự dòng mới theo sau bằng cách xóa luồng. Lưu ý rằng bạn chỉ nên sử dụng std::endlchỉ khi bạn thực sự muốn xóa đầu ra. Không sử dụng std::endl khi bạn thực sự chỉ muốn tạo một dòng kết thúc! Chỉ cần viết một ký tự dòng mới cho sau này!

Trong mọi trường hợp, nếu bạn chỉ viết một vài nhân vật mà không gây đệm đệm luồng của tràn, không có gì sẽ xảy ra với họ cho đến khi họ là một trong hai ngầm hay rõ ràng đỏ mặt. Nói chung, đây không phải là vấn đề vì trường hợp thông thường nơi đầu ra đệm cần có sẵn, tức là khi đọc từ std::cin, được xử lý bởi std::couttie() d đến std::cin. Khi có các cơ chế I/O khác đang được sử dụng, có thể cần phải rõ ràng tie() luồng liên quan. Bộ đệm đôi khi là một vấn đề nếu chương trình "treo" hoặc xác nhận trong quá trình gỡ lỗi vì một số kết quả đầu ra cho luồng có thể đã được tạo nhưng chưa được gửi đi. Biện pháp khắc phục cho việc này là sử dụng std::unitbuf.

+3

Wow. Tôi đang đọc cuốn sách của bạn 'Thư viện chuẩn C++' ngay bây giờ! – rbtLong

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