Zatím jsem si jen pročetl článek a nedíval jsem se na zdrojový kód, takže nevím, jak přesně pracuje s de/alokací, nicméně ohledně JSON mám zkušenost z praxe - máme server, který hodně pracuje s JSON dokumenty a obsluhuje tisíce uživatelů. Obvykle to funguje tak, že dojde zpráva, načtou se vnitřní data, vygeneruje se odpověď a příp. uloží upravená vnitřní data - vše v několika JSONech. Při tomto systému je ideální při zpracování zprávy minimalizovat alokace. Ideálně tedy, aby všechny JSON hodnoty měly při zpracování jedné zprávy jeden (sdílený) alokátor (může být třeba tls v C++11 - zpracování jedné zprávy nepřesahuje hranice aktuálního vlákna), který alokaci (malloc/free) snižuje na minimum. V ideálním případě alokuje jeden větší buffer a pokud nepřeteče, celé zpracování zprávy bude mít jen tuto jednu alokaci - a protože vlákno zůstane aktivní pro další zprávy, lze buffer využít znovu, takže počet alokací na zprávu bude nula (resp. 1 / průměrný počet zpráv na vlákno za jeden restart serveru). A díky tomu, že po ukončení zpracování zprávy se všechny JSON struktury uvolní, nebude dlouhodobě docházet k velké fragmentaci.
Souhlas, mrkl jsem se na zdrojové kódy a vypadá to na dynamické alokace na každém kroku. Já bych šel ještě dál a vytvořil bych alokátor, který by dokázal prvník X bytů alokovat přimo na zásobníku, třeba JSONTmpDocument<1024> doc; ...
Další věc je virtuální metody a interface IValue. Nikdy bych JSON knihovnu nenavrhl v tomto duchu a spíš bych se poučil z toho jak dnešní moderní JS-VM pracují s JS, např. použití pouze 8 bytů na JS value, do kterých lze uložit cokoliv (jak double, tak pointer na String, Object nebo Array), jde to, a není to komplikované. Existujou i další věci, které bych určitě zvážil, něco jako hidden class pro velké dokumenty, apod...
Měl jsem verzi, kde jsem měl vlastní alokátory. Měl jsem verzi bez virtuálních metod. Provedl jsem několik měření. Výsledkem bylo, že to mělo absolutně nulový vliv. Záleží samozřejmě na platformě, ale ty rozdíly budou malé. Když nebudu používá virtuální metody, budu tam mít switch/case a z toho pramení... ano bude tam tabulka pro skoky. Co je volání virtuální metody? Tabulka pro skoky... výsledek je ekvivalentní.
Alokátor tam chystám, ale jak říkám, nikdy jsem neměl v ruce alokátor, který by byl výrazně rychlejší, než standardní new třeba ve MS Windows. Dost mě ta měření kolikrát překvapila. Opravdu bývají standardní alokátory dost rychlé, že se optimalizace nevyplatí.
Pokud tam bude alokátor, bude nejspíš globální, cokoliv jiného věci jen komplikuje. Mohu demonstrovat, když bude zájem. Mám několik serverů pracující s protokolem JSONRPC, takže tak nějak vím o čem to je :)
Vždycky člověk bojuje s tím, jestli udržet kód maximálně KISS, maximálně standardní, nebo ho zaplevelit ručními optimalizacemi.
Děkuji za názor.
V takovém případě bylo něco špatně na tom měření. Custom alokátory, které jsou navržené pro specifické účely (v našem případě pro short-lived data struktury) jsou na tom vždycky o moc líp než globální alokátory, které musí být thread-safe. Nebo jinak, ještě jsem neviděl situaci, kdy by globální alokátor překonal ten lokální.
Tohle dávno neplatí. Například windows používá low-fragmentation heap To je heap, kdy je paměť rozdělena na pevné bloky podle normované velikosti. Přidělení bloku je atomická operace, nic se nezamyká. Jen když dojdou předpřipravené bloky, rozdělí se další větší blok. Je to tak ďábelsky rychlé, že to lze vyrovnat jen maximálně pool alokátorem, to je alokátor, který reusuje uvolněné bloky. Drží je ve spojovém seznamu a ještě rozdělené podle velikosti. Alokace je opět atomické vydání bloku z listu. Řešit alokátor per thread a domnívat se, že to je rychlejší... dávno ne!
https://msdn.microsoft.com/en-us/library/windows/desktop/aa366750(v=vs.85).aspx
Možná že v linuxu jsou horší časy, nevím. Každopádně když tak na to nasazuju ten alokátor s poolem a reuseuju uvolněné bloky. Je to rychlý jednoduchý primitivní mt safe a funguje samo.
Když už jdete do technikálií, tak switch může být zoptimalizován kompilátorem různě, ale nebude to horší než přečtení hodnoty z jedné statické tabulky a následně nepodmíněný skok.
Nicméně toto vůbec nemusí být bottleneck programu. Bottleneckem COW přístupu můžou být právě alokace, a dokonce se může stát, že to co se často zpracovává nebude v paměti blízko sebe a dojde k missům v nějakých procesorových cachích. Dá se totiž předpokládát, že kompilátor bude umět kód zkompilovat tak, že tabulka virtuálních metod, obzvlášť pokud bude často používaná a nebude měněná, bude v rychlé procesorové cachi, zatímco vlastní zpracovávaná data budou třeba kvůli alokacím ke cachím necitlivá.
No tak to nepřehánějte :)
Volání virtuální metody:
00034493 mov ecx,dword ptr [ebx]
00034495 mov edx,dword ptr [ecx]
00034497 call dword ptr [edx+20h]
Vlastní metoda
--- e:\users\ondra\cpp-projects\immujson\src\imtjson\arrayvalue.cpp ------------
return v.size();
0009CF90 mov eax,dword ptr [ecx+0Ch]
0009CF93 sub eax,dword ptr [ecx+8]
0009CF96 sar eax,2
0009CF99 ret
Na alokace je přidaný alokátor, doimplementovat si vhodný může každý, to přesahuje rámec knihovny. Při COW přístupu se zpravidla dělá jedna alokace pro nový kontejner, následně se tam nasdílí všechny nezměněné prvky. Největší zdržovákem je lock inc a lock dec u počítání referencí. Nicméně je to daň za to, že nemusím vytvářet hluboké kopie a nemusím používat zámky při práci s daty, které jsou sdílené mezi vlákny (při změně DOMu stačí zámek na změnu té sdílené proměnné která to celé drží - něco jako commit změn, dokud změna neni finální, ostatní vlákna vidí původní obsah)
Allocator mohu do knihovny zapracovat maximálně takto
https://github.com/ondra-novak/imtjson/pull/2
Je to globální vlastnost. To nijak neomezuje možnost aby byla TLS, jen si to musí uživatel knihovny doprogramovat (zajistit si mechanismus výběru instance podle threadu). Cokoliv jiného mi přijde komplikované a těžkopádné narušující princip KISS a princip obecnosti.
. Přes alokátor v TLS lze zařídit i alokaci ve stacku, prostě ten prostor ve stacku se zahlásí tomu TLS správci alokátorů a ten k němu bude směrovat požadavky pocházející ze stejného vlákna.
Intenzivně se zabývám programováním zejména v jazyce C++. Vyvíjím vlastní knihovny, vzory, techniky, používám šablony, to vše proto, aby se mi usnadnil život při návrhu aplikací. Pracoval jsem jako programátor ve společnosti Seznam.cz. Nyní jsem se usadil v jednom startupu, kde vyvíjím serverové komponenty a informační systémy v C++
Přečteno 50 630×
Přečteno 23 688×
Přečteno 22 738×
Přečteno 20 718×
Přečteno 17 626×