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.

Příspěvek byl publikován v rubrice Počítače. Můžete si uložit jeho odkaz mezi své oblíbené záložky.

12 komentářů: Nový pohled na komprimaci v době víceprocesorových systémů

  1. Ondřej Súkup napsal:

    Řekl bych že jsi zapomněl na nejlepší možnost … ‚xz‘ a zvlášť ve spojení s parametrem ‚–threads‘

    • Heron napsal:

      Nezapomněl. Jen jsem se kouknul do manu a vidím:

      -T threads, –threads=threads
      Multithreaded compression and decompression are not implemented yet, so this option has no effect for now.

      Možná nějaká novější implementace už umí multithreading, ale já se musím striktně držet toho, co je dostupné.

    • Heron napsal:

      Je tak ze zájmu jsem to zkusil v posledním Debianu testing. Thready stále nefungují, ale co je podstatné, tak holý xz (bez parametrů) to komprimuje asi tak 2.2MB/s. Čímž je možná o něco málo rychlejší než bz2, ale ne o moc.

      Pokud by to umělo thready, tak na quadcore jsme na nějakých 10MB/s. Takže trochu lepší než gzip (který by běžel single thread).

      V čem má být ta výhoda xz? Jen jako lepší náhrada bz2?

  2. lzap napsal:

    Veděl jsem, že lzop je rychlý, ale takhle? Mazec, předělám jeden patch na image-deployment na bare-metal tak, aby používal lzop. Původně jsem to chtěl udělat, ale pak se na to vyprd:

    https://github.com/theforeman/foreman-discovery-image/pull/44

    Zajímalo by mě taky srovnání normálního gzipu/bzipu jak si stojí oproti paralelním variantám i lzopu. Dá se očekávat asi podobný komprimační poměr.

  3. lzap napsal:

    Hodně zajímavá optiona lzopu by byla taková, aby adaptivně upravoval komprimační sílu tak, aby byl průtok dat ideální časově. Asi hodně těžko řešitelná úloha :-)

    • Heron napsal:

      Tohle se dělá u blokových komprimací, kdy se zkusí udělat komprimace na malé části dat a podle toho se rozhodně, zda komprimovat celý blok. Obávám se, že stejné řešení při stream komprimaci by to spíš zpomalovalo.

  4. Pingback: Vytvoření komprimovaného obrazu disku | Heronovo

  5. Pingback: SquashFS, aneb proč jsem neznal xz | Heronovo

Komentáře nejsou povoleny.