Přeskočit na obsah
Tech-Blog Chatujme.cz Chatujme.cz
Programování

PHPStan 2.2.10 zakládá paralelní procesy rozvětvením, na Windows spouští nové

PHPStan od vydání 2.2.10 zakládá paralelní procesy rozvětvením toho, který analýzu řídí, místo aby pro každý spouštěl další PHP. Ušetří tím načtení analyzátoru v každém procesu a část paměti nechá sdílenou. Podmínky jsou tři: unixový systém, nativní rozšíření PHPStan Turbo v očekávané verzi a vypnutý OPcache i JIT.

· aktualizováno 1. 9. 2026 · 171 zhlédnutí

Plyšový fialový slon, maskot jazyka PHP
Plyšový elePHPant, maskot jazyka PHP. Tenhle kus rozdávala platforma Heroku. Foto: Atomic Taco, Wikimedia Commons (CC BY-SA 2.0)

Statický analyzátor PHPStan hledá v kódu PHP chyby, aniž by ho spustil. U větších projektů běží analýza v několika procesech naráz a právě tady je změna, kterou přineslo vydání 2.2.10 z 30. srpna 2026: paralelní procesy už nevznikají spuštěním dalšího PHP, ale rozvětvením toho, který analýzu řídí.

Rozvětvení místo spuštění

Rozvětvení procesu, tedy systémové volání fork, vyrobí kopii běžícího procesu i s tím, co má v paměti. Spuštění nového procesu naproti tomu začíná od nuly: čerstvé PHP musí načíst analyzátor, jeho nastavení a autoloader, a teprve pak se dostane k práci. Rozvětvený proces tohle všechno zdědí a stránky paměti mu operační systém sdílí tak dlouho, dokud do nich nezapíše.

Zapnout se to dalo už dřív proměnnou prostředí PHPSTAN_PARALLEL_FORK. Od vydání 2.2.10 je rozvětvení výchozí všude, kde může fungovat. Popis té změny jmenuje dvě podmínky: aktivní nativní rozšíření PHPStan Turbo v očekávané verzi a vypnutý OPcache i JIT. Kde některá z nich neplatí, spouští PHPStan procesy postaru.

Bez nativního rozšíření to nejde

Ta vazba na rozšíření vypadá jako libovůle, ale má konkrétní příčinu ve způsobu, jakým PHP čte archivy phar – a právě z takového archivu se PHPStan obvykle pouští. Knihovna phar drží na každý archiv a proces jediný otevřený popisovač souboru a čtení každé položky přes něj obslouží jako přesun a čtení. Po rozvětvení sdílejí všechny procesy tentýž popis otevřeného souboru, tedy jeden jediný kurzor v jádře. Souběžná čtení se proto proplétají a vracejí bajty z cizích míst; navenek to vypadá jako chyby při zpracování souborů uvnitř archivu. Oprava v samotném PHP podle autorů neexistuje a uživatelský kód se k tomu popisovači nedostane.

Rozšíření Turbo proto navěsí přes pthread_atfork() dvojici obsluh. První si těsně před rozvětvením poznamená kurzor u každého popisovače, který je nad archivem otevřený jen pro čtení. Druhá běží v potomkovi: archiv otevře znovu, kurzor obnoví a přes dup2() podstrčí soukromý popis pod původní číslo popisovače. Do vnitřností phar nesahá ani jedna. Test přiložený k rozšíření ukazuje obojí vedle sebe – bez té ochrany se čtení pokazí rodiči i všem osmi potomkům, s ní projde táž zátěž bez chyby.

Vedlejším ziskem je, že u instalací z archivu je nově urychlený i hlavní proces. Rozšíření se do něj dostane tak, že se hlavní proces sám znovu spustí přes pcntl_exec() s načtenou binárkou; potomci ho pak zdědí. Dosud se rozšíření vpravovalo až do spouštěných procesů parametrem na příkazové řádce, kam rozvětvený proces nedosáhne.

Na Windows se nemění nic

Rozvětvení procesu má v PHP na starosti modul Process Control a ten na Windows k dispozici není: dokumentace jazyka u něj rovnou uvádí, že mimo unixové platformy nepracuje. PHPStan tam proto zůstává u spouštění nových procesů.

Rozšíření Turbo samo přitom Windows podporuje. Balíček phpstan/phpstan podle popisu rozšíření vozí předkompilované binárky pro Linux, macOS i 64bitové Windows, a to pro PHP 8.3 a novější. Analýzu na Windows tedy zrychlí, na způsobu zakládání procesů ale nezmění nic.

Kolik to ušetřilo

Měření proběhlo na dvou velkých projektech, pokaždé s prázdnou vyrovnávací pamětí a se stejnou binárkou rozšíření u obou variant:

projekt a metrikaspouštěnírozvětvení
Slevomat, čas na hodinách105,5 s94,0 s
Slevomat, čas procesoru714,4 s652,6 s
ShipMonk, čas procesoru1 772,4 s1 631,2 s
ShipMonk, čas na hodinách282,1 s317,6 s

První tři řádky znamenají zlepšení o 10,9, 8,7 a 8,0 procenta. Poslední řádek jde proti nim a autor ho v poznámce vysvětluje tím, že měřicí stroj byl v tu chvíli vytížený něčím jiným; za stabilní údaj označuje čas procesoru, který se zlepšil na obou projektech.

Rozvětvení hned přineslo vlastní chybu

Zaváděcí soubory, které si projekt nechává provést před analýzou, běžely dosud jednou v hlavním procesu. Jakmile si takový soubor otevřel spojení do databáze, zdědili ho po rozvětvení všichni potomci a prali se o jediný socket; hlásilo se to jako neúspěšné založení transakce. Oprava, která je součástí téhož vydání, ty soubory provede v každém rozvětveném procesu zvlášť. Spouštěné procesy tenhle problém nikdy neměly, protože každý z nich ty soubory provedl sám.

Skenování adresářů bez vyrovnávací paměti

Druhá velká změna se týká rejstříku symbolů, který si PHPStan staví průchodem přes soubory v adresářích. Nejdražší část toho průchodu, čištění souboru od komentářů a řetězců, byla dosud cyklus přes jednotlivé bajty napsaný v PHP; nově ji dělá Turbo nativně. Na korpusu o 50 MB a 16 800 souborech klesl čas té části z 1,321 s na 0,111 s a čas celého sestavení rejstříku z 1,705 s na 0,445 s.

Podstatnější než ta čísla je, co z nich plyne. Vyrovnávací paměť rejstříku se zrušila celá: ověřit ji totiž znamená spočítat kontrolní součet každého souboru ve stromu, což na tomtéž korpusu trvá 0,419 s, tedy skoro stejně jako nové oskenování. Zmizel s ní i zámek, o který se paralelní procesy dělily. Na jednom z měřených projektů to ubralo 101,7 MB nejvyšší obsazené paměti a směrodatná odchylka mezi opakovanými běhy klesla ze 7,09 s na 1,41 s.

Číslo o spotřebě paměti nešlo ověřit

Do třetice opravuje vydání číslo, které analyzátor tiskne v podrobném režimu. Řádek Used memory vznikal jako součet nejvyšší spotřeby všech procesů a k tomu okamžitá spotřeba hlavního procesu, odečtená ve chvíli, kdy skončil ten první z nich. Procesy ale nemají vrchol ve stejný okamžik, takže ten součet popisoval stav, který nikdy nenastal.

U rozvětvených procesů se k tomu přidala vlastnost PHP, kterou stojí za to znát i mimo PHPStan: hodnotu z memory_get_peak_usage() potomek po rozvětvení dědí, takže proces, který si nic nealokoval, hlásí vrchol svého rodiče. Na jednom projektu se to sečetlo na 29,21 GB, přestože největší proces měl podle jádra vrchol 3,14 GB a součet skutečných vrcholů všech procesů byl 16,69 GB. Nově se tisknou dvě čísla zvlášť: vrchol hlavního procesu a vrchol největšího z rozvětvených.

Co z toho plyne pro práci

Kdo pouští PHPStan na Linuxu nebo macOS z balíčku nainstalovaného Composerem a má PHP 8.3 nebo novější, dostane rozvětvení bez jediného zásahu do nastavení. Kdo pracuje na Windows nebo si nechává zapnutý OPcache, zůstane u starého způsobu a rozdíl neuvidí. Výsledek analýzy je v obou případech stejný – autor u každého měření uvádí, že chybové výpisy byly bajt po bajtu shodné, a rozšíření se dá kdykoli vypnout proměnnou PHPSTAN_TURBO=0.

Zdroje

Programování

PHP výkon PHPStan statická analýza

← zpět na výpis