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