bafrováníTakéBuffer, Vyrovnávací paměť, BufferingPokročilý

Definice

Bufferování je dočasné shromažďování dat ve vyhrazené oblasti paměti (bufferu) mezi dvěma částmi systému, které pracují různou rychlostí nebo po různě velkých dávkách. Zápis se hromadí, dokud se buffer nezaplní nebo dokud ho program nevyprázdní, a teprve pak odchází dál. Cílem je méně drahých operací a plynulejší tok dat.

Kategorie: Softwarový vývojAktualizováno

Nezaměňujte: Bufferování označuje jak dočasné ukládání dat v paměti mezi rychlejší a pomalejší komponentou, tak předplnění datové zásoby u streamovaného přehrávání.

Proč se data nepřenášejí rovnou

Bufferování řeší nesoulad tempa. Zápis jednoho bajtu na disk nebo do síťového socketu stojí prakticky stejně jako zápis čtyř kilobajtů, protože největší část ceny tvoří systémové volání, přepnutí do jádra a režie zařízení. Buffer proto data zadrží v paměti procesu a odešle je až v jedné velké dávce. Stejný princip funguje i opačným směrem: čtečka si načte větší blok dopředu, aby aplikace mohla brát data po řádcích bez dalších volání jádra.

Kdy se buffer vyprázdní

Vyprázdnění (flush) nastává typicky ve čtyřech situacích: buffer je plný, program o flush explicitně požádá, proud se zavírá, nebo je nastaven režim řádkového bufferování a objeví se konec řádku. Standardní knihovny rozlišují tři režimy: bez bufferu (každý zápis jde okamžitě dál), řádkový buffer (obvyklé u terminálu) a plný blokový buffer (obvyklé při přesměrování výstupu do souboru). Právě tahle změna režimu vysvětluje, proč se logy z kontejneru objeví okamžitě na terminálu, ale při přesměrování do souboru zůstanou viset až do konce běhu programu.

Cena, kterou bufferování má

Bufferování kupuje propustnost za latenci a za riziko ztráty. Data v bufferu ještě nikde nejsou: pokud proces spadne nebo je zabit signálem KILL, obsah uživatelského bufferu se ztratí. I po systémovém volání zápisu data často leží jen ve stránkové cache jádra a na plotnu či flash se dostanou až po fsync. Databáze proto kombinují vlastní buffer pool s řízeným vynucením zápisu žurnálu při commitu.

Buffer versus cache

Buffer i cache jsou paměť navíc, ale mají jiný účel. Buffer vyrovnává rozdíl v rychlosti a velikosti dávky u dat, která tečou jedním směrem a obvykle se použijí jednou. Cache uchovává data, u nichž se očekává opakované čtení, a řeší otázku, co při zaplnění vyhodit. Linux si obojí drží v jedné stránkové cache, což vede k tomu, že sloupce buffers a cached ve výpisu paměti bývají zaměňovány.

Bufferování v přenosu médií

U streamovaného videa plní buffer roli nárazníku proti kolísání sítě. Přehrávač si drží několik sekund dopředu, a když propustnost krátce klesne, divák to nepozná. Když zásoba dojde, přehrávání se zastaví: to je ono běžné „bufferuje se“. Delší buffer znamená odolnější přehrávání, ale větší zpoždění, což je u živých přenosů nepřijatelné.

Přetečení bufferu jako bezpečnostní problém

Buffer o pevné velikosti v jazycích bez kontroly mezí je zdrojem zranitelnosti zvané buffer overflow. Zápis za konec vyhrazené oblasti přepíše sousední data, v horším případě návratovou adresu na zásobníku, a útočník tím může přesměrovat běh programu. Obrana stojí na kontrolovaných funkcích s uvedenou délkou, ochranách typu ASLR a stack canary, a hlavně na jazycích, které meze hlídají samy.

Příklady z praxe

  1. Logy v kontejneru se objeví až na konci

    Python při přesměrování stdout do souboru nebo do logu kontejneru přepne na plné blokové bufferování, takže výpisy zůstanou v paměti procesu. Pokud kontejner spadne, logy se ztratí, aniž by se cokoli zapsalo. Řešením je vypnout bufferování proměnnou PYTHONUNBUFFERED nebo si flush vynutit ručně.

    # buffer zůstane nevyprázdněný až do konce běhu
    print("start dávky")
    
    # vynucené odeslání ihned
    print("start dávky", flush=True)
    
    # nebo globálně: PYTHONUNBUFFERED=1 python app.py
  2. Zápis souboru po řádcích versus po blocích

    Program, který volá zápis pro každý řádek CSV zvlášť, provede statisíce systémových volání a běží řádově pomaleji než verze s bufferem. Zabalení proudu do bufferovaného zapisovače sníží počet volání na počet naplnění bufferu. Rozdíl je patrný hlavně u síťových a síťově připojených úložišť.

    const fs = require('fs');
    const out = fs.createWriteStream('export.csv', { highWaterMark: 64 * 1024 });
    
    for (const row of rows) {
      if (!out.write(row + '\n')) {
        // buffer je plný, počkáme na jeho odtečení
        await new Promise(r => out.once('drain', r));
      }
    }
    out.end();

Časté omyly

MýtusKdyž zápis do souboru vrátí úspěch, data už jsou bezpečně na disku.
Ve skutečnostiÚspěšný zápis obvykle znamená jen to, že data přijalo jádro do stránkové cache. Trvanlivost na fyzickém médiu zaručí až fsync nebo otevření souboru v synchronním režimu.
MýtusBuffer a cache jsou dvě slova pro totéž.
Ve skutečnostiBuffer vyrovnává rozdíl v tempu a velikosti dávky u jednosměrně tekoucích dat, cache uchovává data kvůli opakovanému čtení a řeší strategii vyhazování. Linux je jen drží ve společné stránkové cache, což ten dojem vytváří.
MýtusVětší buffer vždycky znamená lepší výkon.
Ve skutečnostiZa určitou hranicí přestává růst propustnost a roste jen latence a spotřeba paměti. U streamování nebo interaktivních protokolů může příliš velký buffer výsledek přímo zhoršit, viz jev bufferbloat v síťových frontách.

Časté dotazy

Jak vypnout bufferování výstupu v Pythonu?
Bufferování výstupu v Pythonu vypnete třemi způsoby. Nejjednodušší je proměnná prostředí PYTHONUNBUFFERED nastavená na libovolnou neprázdnou hodnotu, což je běžná praxe v Dockerfile. Druhou možností je spuštění interpretu s přepínačem -u. Třetí variantou je vynutit odeslání jednotlivě, tedy print s parametrem flush nastaveným na True, případně zavolat metodu flush přímo na proudu. Poslední varianta je nejúspornější: nechá bufferování zapnuté a synchronizuje jen na místech, kde na okamžitém výpisu opravdu záleží.
Jak velký buffer je vhodné zvolit?
Velikost bufferu se obvykle volí jako násobek velikosti bloku úložiště nebo stránky paměti, tedy typicky 4 až 64 kilobajtů. Pod čtyři kilobajty roste podíl režie systémových volání, nad několik set kilobajtů už propustnost prakticky neroste a jen se zvyšuje latence a paměťová stopa. U síťových přenosů hraje roli také součin propustnosti a doby odezvy: buffer menší než toto číslo nedokáže linku plně vytížit. Rozumný postup je začít výchozí hodnotou knihovny a měnit ji až podle měření.
Proč video při přehrávání bufferuje i na rychlém připojení?
Bufferování videa nastává vždy, když se zásoba dopředu načtených dat vyčerpá rychleji, než přehrávač stihne stáhnout další segment. Příčinou nemusí být pomalé připojení, ale krátkodobé výkyvy latence, přetížený server nebo CDN uzel, agresivní přepnutí na vyšší bitrate v adaptivním streamingu, případně zahlcená domácí Wi-Fi. Pomáhá delší předstih bufferu, jenže ten zvyšuje zpoždění, což je u živých přenosů nežádoucí. Přehrávače proto neustále hledají kompromis mezi velikostí zásoby a zpožděním.
Souvisí bufferování s buffer overflow?
Buffer overflow využívá stejnou datovou strukturu, ale jde o chybu, ne o techniku. Přetečení nastane, když program zapíše více bajtů, než kolik má vyhrazená oblast paměti, a přepíše tím sousední data nebo návratovou adresu na zásobníku. Bufferování jako technika optimalizace přenosu je bezpečné, dokud kód hlídá meze. Riziko se týká hlavně jazyků bez automatické kontroly rozsahu, typicky C a C++. Obranou jsou funkce s explicitní délkou, ochrany jako stack canary a ASLR a použití jazyků s bezpečnou prací s pamětí.

Zdroje

  1. Data Buffer(otevře se v novém okně)Wikipedia
  2. Python Standard Library: io - Core tools for working with streams(otevře se v novém okně)Python Software Foundation
  3. Node.js Stream API documentation(otevře se v novém okně)OpenJS Foundation
  4. Buffer Overflow(otevře se v novém okně)OWASP
  5. PostgreSQL Documentation: Server Configuration - Resource Consumption(otevře se v novém okně)PostgreSQL Global Development Group

Související pojmy

Potřebujete to vyřešit v praxi?

Poradíme, jak na to ve vašem projektu

Vysvětlit pojem je jedna věc, navrhnout kolem něj funkční řešení druhá. Ozvěte se a probereme, co dává smysl u vás.