GNU/Linux >> Znalost Linux >  >> Linux

Odstranění velkých hashmap s miliony řetězců v jednom vlákně ovlivní výkon v jiném vlákně

Možná by stálo za to uložit pouze jeden std::string pro všechna vaše data dohromady a použijte std::string_view v mapě. To eliminuje spory o mutex, protože je potřeba pouze jedno přidělení paměti. string_view má triviální destruktor, takže k tomu nepotřebujete vlákno.

Tuto techniku ​​jsem již dříve úspěšně používal ke zrychlení programu o 2500 %, ale to bylo také proto, že tato technika snížila celkové využití paměti.


Můžete zkusit použít std::vector pro uložení paměti. std::vector prvky jsou uloženy souvisle, takže to omezí vynechání mezipaměti (viz Co je kód "přizpůsobivý pro mezipaměť"?)

Takže budete mít map<???,size_t> místo map<???,std::string> budete mít k získání svého řetězce ještě jeden směr (což znamená dodatečné náklady na běh), ale umožní vám to iterovat všechny řetězce s mnohem menším množstvím chyb ve vyrovnávací paměti.


Bylo by skvělé, kdybyste znovu vytvořili problém, se kterým se setkáváte s MVCE, a ukázali jej:víte, mnohokrát problém, o kterém si myslíte, je váš problém... není problém.

Jak mohu s jistotou zjistit, že příčinou jsou výše uvedené 2 problémy s pamětí (jakékoli nástroje/metriky?)

Vzhledem k informacím zde bych doporučil použít profiler - gprof (kompilujte s -g -pg), který je základní. Pokud máte k dispozici kompilátor Intel, můžete použít vtune.

Existuje bezplatná verze vtune, ale já osobně používám pouze komerční verzi.

Kromě toho můžete do kódu vložit časování:z textového popisu není jasné, zda je čas k naplnění mapy srovnatelný s časem potřebným k jejímu vymazání, nebo při souběžném běhu neustále roste. Začal bych tím, kdyby. Všimněte si, že aktuální verze malloc() je také značně optimalizována pro souběžnost (je to Linux? - přidejte k otázce značku).

Když mapu smažete, určitě tam budou miliony free() 's voláno std::~string() - ale musíte si být jisti, že to je problém nebo ne:můžete použít lepší přístup (mnoho zmíněno v odpovědích/komentářích) nebo vlastní alokátor podporovaný obrovským paměťovým blokem, který vytvoříte/zničíte jako jeden celek.

Pokud uvedete MVCE jako výchozí bod, já nebo ostatní budeme schopni poskytnout konzistentní odpověď (toto není odpověď, ale je příliš dlouhá na to, aby to byl komentář)

Jen pro upřesnění, program záměrně nikdy nepřiděluje věci a zároveň neuvolňuje ostatní a má pouze 2 vlákna, jedno vyhrazené pro pouhé smazání.

Mějte na paměti, že každý řetězec v mapě potřebuje jeden (nebo více) new a jeden delete (na základě malloc() a free() respektive), což jsou řetězce buď v klíčích nebo v hodnotách.

Co máte v "hodnotách" mapy?

Protože máte map<string,<set<int>> máte mnoho alokací:Pokaždé, když provedete map[string].insert(val) nového klíče, váš kód implicitně zavolá malloc() pro strunu i sadu. I když je klíč již na mapě, nový int v sadě vyžaduje přidělení nového uzlu v sadě.

Takže při vytváření struktury máte opravdu mnoho alokací:vaše paměť je na jedné straně velmi fragmentovaná a váš kód se zdá být opravdu „intenzivní na malloc“, což by v zásadě mohlo vést k tomu, že volání paměti budou hladovět.

Vícevláknové alokace/přidělení paměti

Jednou zvláštností moderních paměťových subsystémů je, že jsou optimalizovány pro vícejádrové systémy:když jedno vlákno alokuje paměť jednomu jádru, neexistuje globální zámek, ale lokální zámek pod vláknem nebo lokální zámek jádra pro lokální fond vláken. .

To znamená, že když jedno vlákno potřebuje uvolnit paměť přidělenou jiným, jedná se o nelokální (pomalejší) zámek.

To znamená, že nejlepší přístup je, že každé vlákno alokuje/uvolňuje svou vlastní paměť. Řekl, že v zásadě můžete optimalizovat hodně váš kód s datovými strukturami, které vyžadují méně interakcí malloc/free, bude váš kód lokálnější s ohledem na alokaci paměti, pokud každému vláknu dovolíte:

  • získáte jeden blok dat
  • sestavte map<string,<set<int>>
  • uvolnit

A máte dvě vlákna, která opakovaně provádějí tento úkol.

POZNÁMKA:Pro práci se souběžnými vyhodnocovacími zařízeními potřebujete dostatek paměti RAM, ale nyní již používáte 2 z nich současně nabité schématem dvojitého ukládání do vyrovnávací paměti (jedno plnění, jedno čištění). Jste si jisti, že váš systém neprobíhá swapování kvůli vyčerpání RAM?

Navíc je tento přístup škálovatelný:můžete použít tolik vláken, kolik chcete. Ve vašem přístupu jste byli omezeni na 2 vlákna – jedno budování struktury, jedno její zničení.

Optimalizace

Bez MVCE je těžké dát pokyny. Jen nápady, o kterých jen víte, zda je nyní lze použít:

  • nahraďte sadu seřazeným vektorem, rezervovaným v době vytvoření
  • nahraďte klíče mapy plochým vektorem rovnoměrně rozmístěných, seřazených řetězců
  • ukládejte řetězcové klíče postupně v plochém vektoru, přidejte hash, abyste měli přehled o klíčích mapy. Přidejte hash-mapu, abyste měli přehled o pořadí řetězců ve vektoru.

Linux
  1. Vykonat řadu příkazů jedním sudem?

  2. Jak nahradit jeden znak druhým ve všech názvech souborů aktuálních adresářů?

  3. Smazání všech C komentářů se Sedem?

  1. Spojte dva řetězce v jednom řádku s grep

  2. Přístup k místnímu vláknu z jiného vlákna

  3. Manipulace s pamětí pomocí struktury epoll_event

  1. Zlepšete výkon systému Linux pomocí noatime

  2. Hledání obsahu jednoho souboru v jiném souboru

  3. Nastavení DRBD pouze s jedním uzlem