Heronovo IT doupě

Nový pohled na komprimaci v době víceprocesorových systémů

Během nedávného update jednoho projektu přišla na přetřes také otázka případné změny zálohování, vylepšení komprimaci, rychlosti a vůbec.

Původní řešení je jednoduché, poměrně rozšířené a bezvadně funkční:

 export_dat | gzip > /storage/soubor_zalohy

Pochopitelně na tom není nic špatného. Jenže, je to řešení z doby, kdy průměrný počet procesorů byl 1. Počet procesorů nám narostl a i v poměrně levném hw můžeme mít 24 procesorových jader (z čehož marketing hravě udělá 48). Což bohužel na testy nemám, takže se spokojíme se 4.

Jedno jádro má poměrně omezený výkon a bez ohledu na to, jak rychle je schopen dodávat data ten export_dat (v našem konkrétním případě asi 350 MiB/s), tak rychlost komprimace klasického gzipu je max 9 MiB/s. Což je vzhledem k velikosti dat poměrně málo.

Takže začalo hledání jiných možností. Možnost, která se okamžitě nabízí a navíc je krásně unixová je spustit daný export několikrát (rozdělit data na balíky) a pustit to paralelně vedle sebe. Což je sice teoreticky pěkné, ale ne vždy možné (v našem případě by to šlo, ale o tom jindy).

Je tedy nutné nahradit ten gzip nějakým (ideálně gzip kompatibilním) multithread komprimátorem (jak se krásně hezky česky říká).

Poznámka: Tento článek nemám být ultimativním testem dostupných komprimačních nástrojů. Takových jsou na webu mraky. Tento článek pojednává o konkrétní a poměrně rozšířené situaci exportu dat z nějaké aplikace přes síť. Je zde tedy bottlneck v podobě rychlosti zápisu dat na síť, který pochopitelně ovlivňuje výsledky. Což je v tomto případě žádoucí a chtěné.

Co je k disposici

  • lzop – pekelně rychlý, rychlejší než gigabitová síť (pro jednoduchost 100 MB/s i když v praxi lze dosáhnout saturace na 115 MB/s), tady rychlejší paralelní implementace není potřeba, bottlneck je jinde. Neplést si s Lzapem (ten je taky skvělý, ale k proudové komprimaci ne zcela vhodný).
  • gzip – Starý známý, poměrně pomalý (8.75 MB/s).
  • pigz – Multithreadová varianta gzipu (na quadcore 30 MB/s, tedy skoro 4x tolik).
  • bzip2 – Vůbec nejpomalejší, nejlepší komprimační poměr (rychlost 2 MB/s).
  • pbzip2 – Paralelní varianta bzip2 (rychlost 8.1 MB/s, tedy skoro jako single gzip, ale za značnou cenu, k tomu se dostaneme).

Balíčky s pigz i pbzip2 jsou v Debianu a určitě v i dalších distrech.

Krom lzopu jsem testoval jen multithread varianty. Jen čistý neparalelní bzip2 by běžel přes 6h, tolik času jsem tomu věnovat nemohl a ani nechtěl (fakt nebudu testovat něco, o čem vím, že je to pomalé a že to stejně v projektu nepoužiju).

Komprimační poměr

  • Surová data: 136 GiB (100%)
  • lzop: 97 GiB (71%)
  • pbzip2: 47.4 GiB (35%)
  • pigz: 53.9 GiB (40%)

Bez překvapení, nejlépe komprimuje pbzip2. Jenže za jakou cenu:

Časová náročnost

  • Export bez komprimace, omezeno rychlostí sítě: 1397s (100%)
  • lzop: 990s (71%) (ano, díky komprimaci se po síti přenese méně dat a je to rychlejší), omezeno rychlostí sítě.
  • pbzip2: 5841s (418%) !!!
  • pigz: 1794s (128%)

Tedy, v případě pbzip2 máme sice nejlepší komprimační poměr, ale zaplatíme za to 4x delší dobou komprese. A jen o 5 procentních bodů horší pigz nám čas exportu prodlouží o snesitelných 28% (proti čistému exportu bez komprimace).

Lzop se výborně hodí v případech, že data potřebujeme rychle protáhnout bottlneckem sítě.

Odhad doby komprimace bzip2 a gzip

Samotné staré známé bzip2 a gzip jsem netestoval, je to zbytečné. Přesto, pojďme se podívat, co by nám byly schopni nabídnout. Nejprve si vysvětleme, odkud se vzala následující čísla.

Testy jsem spouštěl:

time export | komprimator | pv > /sitovy/soubor

Pokud neznáte pv, vyzkoušejte. Program pv je jen roura, která stdin pošle na stdout a s daty nic nedělá. Zobrazuje ale statistiky, aktuální rychlost proudění dat přes rouru a celkovou velikost dosud přenesených dat.

Získané časy z utilitky time jsou: real, user (a system). Čas real je skutečný čas běhu programu (hodiny na zdi), čas user je čas procesoru (a čas system je čas v jádře, to tady nehraje roli, časy jádra byly pod 60s a do výsledků jsem je nezahrnoval = systematická chyba 1%, neřeším).

Pro single thread platí, že: real < user + system  (procesor nemůže strávit víc času, než reálně uběhlo).

U multithread toto ale neplatí. user (a system) je součet časů jednotlivých procesorů a je dobře, že je to víc než real.

Odhady

pbzip2: Real = 5841s, User = 22276s (6.2h)
Lze tedy odhadovat, že klasický bzip2 by běžel těch 6.2h!!!

pigz: Real = 1794s, User = 6245s
Lze odhadovat, že klasický gzip by běžel 1.7h.

Velmi pěkné je i urychlení, testy běžely na čtyřjádře, ideální urychlení je tedy 4.0, reálně:
pbzip2: 3.8x
pigz: 3.5x

Poznámka pro hnidopichy: Na stejném CPU také běžel datový server, který zatěžoval jedno jádro max 17%. Komprimátorům bylo dostupných tedy teoretických 3.83 CPU. Nějaké detailní testy a exaktní výpočty jsou ale zbytečné, vlivy v reálném prostředí všechny tyto odhady a chyby hravě zahladí.

Tož tak

Takže v tomto případě je tedy ideální nahradit gzip za pigz. Narážet na strop sítě se nebude, časy se zkrátí 3.5x. Z reálných 6 hodin exportu se může snadno stát necelých 2h.

Navíc to splňuje onu podmínku, aby exportovaná data byla v něčem gzip kompatibilním (z důvodu nerozbití obnovení).

Otázkou je, jak dobře to škáluje. Kdyby se tomu přihodily další procesory, tak u 12 cpu by se mohlo začít narážet na limit daný sítí a teoreticky by se to stihlo za půl hoďky.