2012-02-14 8 views
14

Czy ktoś może wyjaśnić nieco lepiej pojęcie buforów? Rozumiem, że bufory to struktury danych, w których przechowywane są znaki, oraz miejsce, w którym dane mają być odczytywane. Jaka jest idea buforowania płukania?C++ bufory cout i cin oraz bufory ogólnie

Kiedy bufor jest przepłukiwany, jest to związane z pisaniem zapisanych w nim znaków?

Z tekstu:

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. 

Odnosząc się do „spłukiwania”, które sprawia, że ​​brzmi prawie jak jeśli bufor jest napisane, ale również usuwane w tym samym czasie. Tylko spekulacje.

Tak więc, aby napisać do wyświetlenia na ekranie, wymagane jest płukanie bufora?

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. 

Tutaj brzmi to tak, jakby za pomocą „endl” na końcu mówi, że system musi natychmiast napisać (co sugeruje inaczej znaczy nie?) Jaka jest endl nie jest używany?

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

to pytanie zostało zadane i udzielono odpowiedzi na wiele pytań w SO. http://stackoverflow.com/a/4752069/1155650 –

Odpowiedz

2

myśleć, co by się stało, jeśli za każdym razem „napisał” bajt do pliku na dysku, program rzeczywiście udał się do dysku, przeczytać w bieżącym sektora/klastra/bloku, zmienił jeden bajt następnie zapisał go na dysku fizycznym.

Powiedziałbym oprogramowanie będzie najlepiej opisany jako „lodowaty” pod względem wydajności :-)

buforowania jest sposobem dozowania up odczytuje i zapisuje, aby uczynić je bardziej wydajne.

Na przykład, jeśli piszesz do pliku na dysku, może poczekać, aż masz pełny blok 4K przed zapisaniem go na dysku.

Podczas czytania może otrzymać blok 4K, nawet jeśli prosiłeś o dziesięć bajtów, przy założeniu, że wkrótce możesz poprosić o resztę.

Płukanie przy zapisie odbywa się niejawnie, gdy pamięć podręczna jest pełna lub jawnie, na żądanie (z wywoływaniem flush lub zamknięciem pliku).

Należy pamiętać, że buforowanie tego pliku to tylko jeden rodzaj buforowania, którego można używać w dowolnym miejscu, w którym można uzyskać wzrost wydajności przez "dzielenie" odczytów i zapisów.

21

Podstawową ideą buforowania jest łączenie operacji w większe części: zamiast czytać małą liczbę bajtów, przeczytaj całą stronę i udostępnij ją zgodnie z wymaganiami; zamiast zapisywać małą liczbę bajtów, buforuj je i pisz całą stronę lub gdy wyraźnie zażądasz zapisu. Zasadniczo jest to ważna optymalizacja wydajności. Jest całkiem czytelny dla operacji we/wy, ale generalnie odnosi się również do innych zastosowań: przetwarzanie wielu jednostek na raz generalnie kończy się szybciej niż przetwarzanie pojedynczych jednostek.

W odniesieniu do I/O spłukiwanie odnosi się do zapisywania aktualnie buforowanych bajtów do miejsca docelowego - cokolwiek to oznacza w praktyce. Dla C++ IOStream płukanie strumienia oznacza wywołanie funkcji składowej std::ostream::flush(), która z kolei wywołuje std::streambuf::pubsync() na skojarzonym buforze strumienia (to ignoruje szczegóły, że strumień jest w rzeczywistości wzorcami klas, np.std::basic_ostream<cT, traits>; dla celów niniejszej dyskusji nie ma znaczenia, że ​​są szablonami klas): podstawowa klasa std::streambuf jest abstrakcją C++ dotyczącą sposobu przetwarzania określonego strumienia. Koncepcyjnie składa się z bufora wejściowego i wyjściowego oraz funkcji wirtualnej odpowiedzialnej za odczyt lub zapis buforów. Funkcja std::streambuf::pubsync() wywołuje funkcję wirtualną std::streambuf::sync(), którą należy przesłonić dla każdego bufora strumieniowego, który potencjalnie buforuje znaki. Oznacza to, że to, co w rzeczywistości oznacza, to zależy od sposobu implementacji tej funkcji wirtualnej.

czy override sync() rzeczywiście coś robi i co robi wyraźnie zależy od tego bufor strumienia reprezentuje. Na przykład dla std::filebuf, który jest odpowiedzialny za odczyt lub zapis do pliku, sync() zapisuje bieżącą zawartość bufora i usuwa wypróżnione znaki z bufora. Biorąc pod uwagę, że plik może nie odzwierciedlać fizycznego pliku, ale np. Nazwany potok do komunikacji z innym procesem, to rozsądne zachowanie. Z drugiej strony, przepłukanie std::stringbuf, które jest buforem strumieniowym stosowanym do realizacji zapisu do użytego std::string, np. przez std::ostringstream w rzeczywistości nic nie robi: ciąg jest w całości w programie, a std::string reprezentujący jego wartość jest konstruowany po wywołaniu funkcji składowej std::stringbuf::str(). Dla zdefiniowanych przez użytkownika buforów strumieni istnieje wiele różnych zachowań. Typową klasą buforów strumieniowych jest filtrowanie wyjścia w pewien sposób przed przekazaniem go do innego bufora strumieniowego (np. Bufor rejestrujący może dodać znacznik czasu po każdym nowym wierszu). Zwykle nazywają one funkcję std::streambuf::pubsync() następnych buforów strumieni.

W związku z tym opisy faktycznego zachowania są zwykle utrzymywane dość niejasne, ponieważ nie jest jasne, co dokładnie się dzieje. Koncepcyjnie, przepłukanie strumienia lub wywołanie pubsync() w buforze strumieniowym powinno zaktualizować zewnętrzny cel znaku, aby dopasować go do bieżącego stanu wewnętrznego. Ogólnie rzecz biorąc, oznacza to przekazywanie aktualnie buforowanych znaków i usuwanie ich z wewnętrznego bufora. W tym miejscu warto zauważyć, że bufor jest zwykle zapisywany, gdy jest po prostu pełny. Kiedy stanie się pełny, zależy ponownie od konkretnego bufora strumieniowego. Dobra implementacja std::filebuf zasadniczo wypełni bufor bajtów odpowiadający rozmiarowi bazowej strony (lub jego wielokrotności), a następnie zapisze pełne strony, minimalizując liczbę operacji we/wy potrzebnych (jest to stosunkowo trudne, ponieważ bufor rozmiary różnią się między różnymi systemami plików iw zależności od kodowania używanego przy pisaniu liczby produkowanych bajtów nie da się łatwo oszacować).

Standardowa biblioteka C++ zwykle nie wymaga wyraźnych flushes:

  • strumienia std::cerr jest skonfigurowany do automatycznego spłukiwania żadnego wyjścia produkowanego po każdej operacji wyjściowe miano. Jest to wynik ustawienia domyślnego flagi formatowania std::ios_base::unitbuf. Aby wyłączyć tę funkcję, możesz użyć std::cerr << std::nounitbuf lub możesz po prostu użyć std::clog, która zapisuje do tego samego miejsca docelowego, ale nie robi tego płukania.
  • Czytając od std::istream „związanym” std::ostream, jeśli występuje, jest opróżniany. Domyślnie std::cout jest związany z std::cin. Jeśli chcesz samemu ustawić powiązanego std::ostream możesz użyć np. in.tie(&out) lub, jeśli chcesz usunąć związany numer std::ostream, możesz użyć np. std::cin.tie(0).
  • Po zniszczeniu std::ostream (lub w przypadku normalnego zniszczenia w przypadku, gdy std::ostream jest jednym ze standardowych strumieni), std::ostream jest przepłukiwany.
  • Gdy bufor strumienia zostanie przepełniony, wywoływana jest funkcja wirtualna std::streambuf::overflow(), która zwykle zapisuje bufor do bieżącego bufora (plus przekazany znak, jeśli taki istnieje). Często odbywa się to po prostu wywołując sync(), aby wyczyścić bieżący bufor, ale to, co się robi dokładnie, znowu, zależy od konkretnego bufora strumienia.

Można również wyraźnie zażądać spłukiwania std::ostream:

  • można wywołać funkcji członka std::ostream::flush(), np std::cout.flush().
  • Możesz wywołać funkcję składową std::streambuf::pubsync(), np. std::cout.rdbuf()->pubsync() (zakładając, że jest ustawiony bufor strumienia, wywołanie std::ostream::flush() nic nie da, jeśli nie ma bufora strumieniowego).
  • Można użyć manipulatora std::flush, np. std::cout << std::flush.
  • Można użyć manipulatora std::endl, np. std::cout << std::endl, aby najpierw wpisać znak nowej linii, a następnie przepłukać strumień. Zauważ, że powinieneś używać std::endltylko, kiedy naprawdę chcesz opróżnić wyjście. Nie używaj używaj std::endl, gdy chcesz po prostu utworzyć koniec linii! Po prostu napisz znak nowej linii dla tego ostatniego!

W każdym razie, jeśli napiszesz tylko kilka znaków, które nie powodują przepełnienia bufora bufora strumieniowego, nic się z nimi nie stanie, dopóki nie zostaną one pośrednio lub jawnie przepłukane. Zasadniczo nie stanowi to problemu, ponieważ normalny przypadek, w którym buforowane wyjście musi stać się dostępne, to jest podczas odczytu z std::cin, jest obsługiwany przez std::cout będący tie() d do std::cin. Gdy są używane inne mechanizmy wejścia/wyjścia, może być konieczne jawne strumieniowanie powiązane. Bufor jest czasem problemem, jeśli program "zawiesza się" lub zapewnia podczas debugowania pewne wyjścia do strumienia, które mogły zostać utworzone, ale jeszcze nie zostały wysłane. Rozwiązaniem tego problemu jest użycie std::unitbuf.

+3

Wow. Czytam teraz twoją książkę "The Standard Library C++"! – rbtLong

Powiązane problemy