Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel

V ekosystému PHP jsou aktuálně dva konektory pro práci se serverem Tarantool - jedná se o oficiální rozšíření PECL tarantool/tarantool-php, napsaný v C a tarantool-php/client, napsaný v PHP. Jsem autorem toho druhého.

V tomto článku bych se rád podělil o výsledky testování výkonu obou knihoven a ukázal, jak s minimálními změnami v kódu můžete dosáhnout 3-5 zvýšení výkonu (na syntetických testech!).

Co budeme testovat?

Otestujeme ty výše zmíněné synchronní konektory běžící asynchronně, paralelně a asynchronně-paralelně. 🙂 Také se nechceme dotknout kódu samotných konektorů. V současné době je k dispozici několik rozšíření, abyste dosáhli toho, co chcete:

  • Swoole ― vysoce výkonný asynchronní rámec pro PHP. Používají ji takoví internetoví giganti jako Alibaba a Baidu. Od verze 4.1.0 se objevila magická metoda SwooleRuntime::enableCoroutine(), který vám umožňuje „převést synchronní síťové knihovny PHP na asynchronní pomocí jednoho řádku kódu“.
  • Async byl donedávna velmi slibným rozšířením pro asynchronní práci v PHP. Proč donedávna? Bohužel z mně neznámého důvodu autor úložiště smazal a další osud projektu je nejasný. Budu to muset použít одним z vidliček. Stejně jako Swoole vám toto rozšíření umožňuje snadno zapnout kalhoty pohybem zápěstí a umožnit asynchronii nahrazením standardní implementace streamů TCP a TLS jejich asynchronními verzemi. To se provádí pomocí možnosti „async.tcp = 1".
  • Paralelní ― poměrně nové rozšíření od známého Joe Watkinse, autora knihoven jako phpdbg, apcu, pthreads, pcov, uopz. Rozšíření poskytuje API pro multithreading v PHP a je umístěno jako náhrada za pthreads. Významným omezením knihovny je, že funguje pouze s verzí PHP ZTS (Zend Thread Safe).

Jak budeme testovat?

Spusťte instanci Tarantool se zakázaným protokolováním napřed (wal_mode = žádný) a větší vyrovnávací paměť sítě (předčítání = 1 * 1024 * 1024). První možnost eliminuje práci s diskem, druhá umožní číst více požadavků z vyrovnávací paměti operačního systému a tím minimalizovat počet systémových volání.

U benchmarků, které pracují s daty (vkládání, mazání, čtení atd.), bude před spuštěním benchmarku (znovu) vytvořen prostor memtx, ve kterém jsou primární hodnoty indexu vytvořeny generátorem uspořádaných celočíselných hodnot ​(sekvence).
Prostor DDL vypadá takto:

space = box.schema.space.create(config.space_name, {id = config.space_id, temporary = true})
space:create_index('primary', {type = 'tree', parts = {1, 'unsigned'}, sequence = true})
space:format({{name = 'id', type = 'unsigned'}, {name = 'name', type = 'string', is_nullable = false}})

V případě potřeby se před spuštěním benchmarku zaplní prostor 10,000 XNUMX n-ticemi formuláře

{id, "tuplе_<id>"}

K nticím se přistupuje pomocí náhodné hodnoty klíče.

Samotný benchmark je jediný požadavek na server, který je proveden 10,000 5krát (otáček), které jsou naopak prováděny v iteracích. Iterace se opakují, dokud všechny časové odchylky mezi 3 iteracemi nejsou v rámci přijatelné chyby 1 %*. Poté se vezme průměrný výsledek. Mezi iteracemi je pauza XNUMX sekunda, aby se zabránilo omezení procesoru. Luův garbage collector je před každou iterací deaktivován a je nucen se spustit po jejím dokončení. Proces PHP se spouští pouze s rozšířeními nezbytnými pro benchmark, s povoleným ukládáním do vyrovnávací paměti výstupu a deaktivovaným garbage collectorem.

* Počet otáček, iterací a prahovou hodnotu chyby lze změnit v nastavení benchmarku.

Testovací prostředí

Níže publikované výsledky byly vytvořeny na MacBookPro (2015), operační systém - Fedora 30 (verze jádra 5.3.8-200.fc30.x86_64). Tarantool byl spuštěn v dockeru s parametrem "--network host".

Verze balíčků:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker: 19.03.3, sestavení a872fc2f86
PHP: 7.3.11 (cli) (vytvořeno: 22. října 2019 08:11:04)
tarantool/klient: 0.6.0
rybakit/msgpack: 0.6.1
ext-tarantool: 0.3.2 (+ patch pro 7.3)*
ext-msgpack: 2.0.3
ext-async: 0.3.0-8c1da46
ext-swoole: 4.4.12
ext-paralelní: 1.1.3

* Oficiální konektor bohužel nefunguje s verzí PHP > 7.2. Pro kompilaci a spuštění rozšíření na PHP 7.3 jsem musel použít náplast.

výsledky

Synchronní režim

Protokol Tarantool používá binární formát MessagePack k serializaci zpráv. V konektoru PECL je serializace skryta hluboko v hlubinách knihovny a ovlivňuje proces kódování z uživatelského kódu se nezdá možné. Čistý konektor PHP naopak poskytuje možnost přizpůsobit proces kódování rozšířením standardního kodéru nebo použitím vlastní implementace. Po vybalení jsou k dispozici dva kodéry, jeden je založen na msgpack/msgpack-php (oficiální rozšíření MessagePack PECL), druhý je zapnutý rybakit/msgpack (v čistém PHP).

Před porovnáním konektorů změříme výkon kodérů MessagePack pro konektor PHP a v dalších testech použijeme ten, který vykazuje nejlepší výsledek:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Přestože je verze PHP (Pure) v rychlosti horší než rozšíření PECL, v reálných projektech bych ji přesto doporučil používat rybakit/msgpack, protože v oficiálním rozšíření MessagePack je specifikace formátu implementována pouze částečně (například chybí podpora vlastních datových typů, bez kterých nebudete moci používat Decimal - nový datový typ představený v Tarantool 2.3) a má množství dalších проблем (včetně problémů s kompatibilitou s PHP 7.4). No, obecně ten projekt vypadá opuštěně.

Pojďme tedy změřit výkon konektorů v synchronním režimu:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Jak je vidět z grafu, konektor PECL (Tarantool) vykazuje lepší výkon ve srovnání s konektorem PHP (Client). To však není překvapivé, protože to druhé, kromě toho, že je implementováno v pomalejším jazyce, ve skutečnosti dělá více práce: s každým voláním je vytvořen nový objekt Žádost и Odpověď (v případě Select - také Kritériaa v případě Update/Upsert ― operace), samostatné entity přípojka, Packer и Psovod přidávají také režii. Je zřejmé, že flexibilita má svou cenu. Obecně však PHP interpret vykazuje dobrý výkon, i když existuje rozdíl, je nevýznamný a možná bude ještě menší při použití předběžného načítání v PHP 7.4, nemluvě o JIT v PHP 8.

Pokračujme. Tarantool 2.0 přidal podporu pro SQL. Zkusme provést operace Select, Insert, Update a Delete pomocí SQL protokolu a porovnejme výsledky s noSQL (binárními) ekvivalenty:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Výsledky SQL nejsou příliš působivé (připomínám, že stále testujeme synchronní režim). Předem bych se tím ale nerozčiloval, podpora SQL je stále v aktivním vývoji (relativně nedávno přibyla např. podpora připravená prohlášení) a soudě podle seznamu otázkySQL engine projde v budoucnu řadou optimalizací.

Async

Nyní se podívejme, jak nám rozšíření Async může pomoci zlepšit výše uvedené výsledky. Pro psaní asynchronních programů poskytuje rozšíření API založené na coroutinech, které budeme používat. Empiricky jsme zjistili, že optimální počet korutinů pro naše prostředí je 25:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
„Rozložte“ 10,000 25 operací do XNUMX korutin a uvidíte, co se stane:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Počet operací za sekundu se zvýšil více než 3krát tarantool-php/client!

Je smutné, že konektor PECL nezačal s ext-async.

A co SQL?

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Jak vidíte, v asynchronním režimu se rozdíl mezi binárním protokolem a SQL dostal na hranici chyb.

Swoole

Opět zjišťujeme optimální počet korutin, tentokrát pro Swoole:
Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Zastavme se na 25. Zopakujme stejný trik jako u rozšíření Async – rozdělte 10,000 25 operací mezi 2 korutin. Navíc přidáme další test, ve kterém veškerou práci rozdělíme do 5,000 dvou procesů (to znamená, že každý proces provede 25 operací ve XNUMX korutinech). Procesy budou vytvořeny pomocí SwooleProcess.

Výsledky:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Swole vykazuje mírně nižší výsledek ve srovnání s Async při spuštění v jednom procesu, ale se 2 procesy se obraz dramaticky mění (číslo 2 nebylo zvoleno náhodou; na mém stroji to byly 2 procesy, které vykazovaly nejlepší výsledek).

Mimochodem, rozšíření Async má i API pro práci s procesy, ale tam jsem nezaznamenal žádný rozdíl od spouštění benchmarků v jednom nebo více procesech (je možné, že jsem se někde popletl).

SQL vs binární protokol:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Stejně jako u Async je v asynchronním režimu eliminován rozdíl mezi binárními a SQL operacemi.

Paralelní

Protože rozšíření Parallel není o korutínách, ale o vláknech, změřme optimální počet paralelních vláken:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Na mém stroji se rovná 16. Spusťte benchmarky konektorů na 16 paralelních vláknech:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Jak vidíte, výsledek je ještě lepší než u asynchronních rozšíření (nepočítáme-li Swoole běžící na 2 procesech). Všimněte si, že pro konektor PECL jsou operace Update a Upsert prázdné. To je způsobeno skutečností, že tyto operace selhaly s chybou - nevím, zda to byla chyba ext-parallel, ext-tarantool nebo obou.

Nyní porovnejme výkon SQL:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel
Všimli jste si podobnosti s grafem pro synchronně běžící konektory?

Spolu

A nakonec shrňme všechny výsledky do jednoho grafu, abychom viděli celkový obrázek pro testovaná rozšíření. Do grafu přidáme jen jeden nový test, který jsme ještě neprovedli – budeme paralelně spouštět Async coroutines pomocí Parallel*. Myšlenka integrace výše uvedených rozšíření již existuje diskutováno autorů, ale nebylo dosaženo konsensu, budete to muset udělat sami.

* Nebylo možné spustit Swoole coroutines s Parallel, zdá se, že tato rozšíření jsou nekompatibilní.

Takže konečné výsledky:

Zrychlení PHP konektorů pro Tarantool pomocí Async, Swoole a Parallel

Místo závěru

Podle mého názoru se výsledky ukázaly jako docela hodné a z nějakého důvodu jsem si jistý, že to není limit! Ať už si o tom musíte v reálném projektu rozhodnout sami, řeknu jen, že pro mě to byl zajímavý experiment, který vám umožní zhodnotit, jak moc se dá ze synchronního TCP konektoru „vymáčknout“ s minimální námahou. Pokud máte nápady na vylepšení benchmarků, rád vaši žádost o stažení zvážím. Veškerý kód s pokyny ke spuštění a výsledky je publikován v samostatném úložišť.

Zdroj: www.habr.com

Přidat komentář