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.