Re: Pomalý přístup u fork-procesu k paměti alokované přes mmap

Pavel Kankovsky peak na argo.troja.mff.cuni.cz
Pátek Prosinec 9 19:46:26 CET 2016


On Fri, 9 Dec 2016, Zdeněk Janiš wrote:

> Alokuji pole přes mmap(), aby bylo možné s touto pamětí pracovat i ve 
> forknutých procesech.
> A překvapil mě fakt, že funkce, která s tímto polem pracuje, je ve forknutém 
> procesu pomalejší než v hlavním procesu. Uniká mi důvod proč to tak je...
>
> V příloze zasílám příklad, na kterém to je vidět. Na mém počítači je rozdíl 
> kolem 50ms. Zajímalo by mě zda se to stejně bude chovat i jinde. Tak pokud 
> budete mít chuť vyzkoušejte a podělte se o výsledky.

Absolutní rozdíl není ani tak zajímavý jako relativní.

Zkusil jsem to na třech různých strojích (různý hardware, různý počet 
CPU, různé verze Linuxu) a koukal jsem jak blázen, protože jsem ve všech 
případech dostal poměr blízký 2:1 v neprospěch forknutých procesů.

Zkusil jsem, co se stane, když průchod pameti provedu v proc() desetkrát a 
výsledek byl docela překvapivý, protože v absolutních číslech zůstal 
rozdíl zhruba stejný a oba výsledky se spíš podobal desetinásobku té 
původní menší hodnoty.

Je tedy zjevné, že se jedná o něco, co se ve forknutém procesu provádí jen 
při jediném průchodu.

Na stroji, který má víc socketů, byly měly naměřené hodnoty největší 
rozptyl, což má podle mne na svědomí NUMA, protože variabilita se výrazně 
snížila, když jsem použil taskset, abych to připoutal k jednomu jádru.

(Když jsem však nechal použít dvě jádra z téhož socketu, tak najednou 
některé forknuté případy byly stejné jako ten hlavní proces?! A tomu fakt 
nerozumím.)

Schválně jsem zkusil spuštění proc() v hlavním procesu přesunout až na 
konec za všechny forky, ale výsledek to nezměnilo a forknuté procesy jsou 
o něco pomalejší. Smazat všechny rozdíly mezi procesy se mi povedlo tím, 
že jsem do zvláštního procesu přestěhoval i inicializaci, který byla 
původně v main().

Celkově mi z toho plyne, že první přístup ke sdílené paměti (přesněji asi 
ke stránce této sdílené paměti) v určitém procesu je nějak penalizován a 
to i v případě, že proces je fork z jiného procesu, který už takový 
přístup provedl.

Zkusil jsem do programu připsat, aby v proc() a začátku a po dokončení 
každého z 10 průchodů zavolal getrusage a vypsat ru_minflt. A je to jasné: 
první průchod v každém jednotlivém procesu udělá hromadu soft page faults.
A pokud byla inicializace přímo v hlavním procesu, tak všechny page faulty 
nastaly tam a čtení už proběhlo hladce.

Finta je nejspíš v tom, že fork() nekopíruje stránkové tabulky, protože by 
to v mnoha případech bylo zbytečné, zejména je-li použito tradiční kombo 
fork-execve, kdy je forknutý proces stejně téměř okamžitě nahrazen jiným 
programem.

-- 
Pavel Kankovsky aka Peak                      "Que sçay-je?"


Další informace o konferenci Linux