K tempu článku dost dobře zpětnou vazbu dát nemohu, nejsem tak úplně cílovka. Nicméně pár drobností tu mám, takže nejprve bych chtěl zdůraznit, že celkově mi článek přijde super, tedy následující kritika nemá za cíl shodit celý text, jen se dotknout problémů, které v něm vidím.
„[...] díky tomu, že mnoho programovacích jazyků nabízí programátorům automatickou správu paměti (garbage collector) se stavový prostor mnohdy dosti razantním způsobem zmenšuje (nemusíme u všech objektů řešit, kdo je jejich vlastník a kdo je může uvolnit z operační paměti) [...]“
Předpokládám, že to tak nebylo myšleno, ale tato část může evokovat dojem, že vlastníka a uvolnění objektů z paměti nemusíme řešit vůbec. Což je právě naopak - jestliže děláme hru v jazyce, který má GC, tak stavět okolo tohoto předpokladu je vyloženě cesta do pekla, které nastane ve chvíli, kdy dojde projekt do oné optimalizační fáze, kdy je potřeba začít zjišťovat, co je v paměti a co ne a proč to tam ještě je. Jasně, v ideálním světě se tato fáze děje celou dobu vývoje, ale hry (kód, data, design, ...) se vyvíjí tak rapidním způsobem, že to není tak úplně reálné.
Takže zde máme fázi rapidního vývoje, kdy se celá hra neustále mění dost zásadním způsobem, pak se přechází do fáze ladění mechanik a až pak nastávají větší optimalizace. A to, podle jakých principů byly vyvíjeny první fáze, se zúčtuje v těch posledních - a to přesně podle poučky, že cena bugu roste s časem exponenciálně.
To, že neřešíme životnost krátkodobých objektů (dočasné objekty a pole), je ok (ale nesmí jich být moc, jinak GC zaseká celou hru - ideální, ale nereálné, je nula alokací během snímku). Ale hned od začátku je vyloženě nutnost řešit život assetů - textury, modely, zvuky, fonty, atd. Musí být naprosto jasné a deterministické, kdy jsou potřeba a kdy už ne.
„func [...] redraw() { [...] moveNPC(state) [...] }“
Tuto chybu jsem naposledy udělal před mnoha lety v mých neslavných začátcích :D. Opravdu, opravdu, opravdu není dobrý nápad aktualizovat stav hry ve funkci pro kreslení scény. Ze začátku se to může zdát v pohodě, jenže pak člověk narazí na spousty problémů, které to způsobuje (např. pauzování hry). Jistě, v triviálním příkladě ze článku je oprava značně jednoduchá - prostě to volání přesunout z redraw() do eventLoop() hned před state.redraw().
Jenže článek se mi jeví, jako pro začátečníky (když už ne v rámci programování, tak v rámci vývoje her) a přijde mi trochu nešťastné jim ukazovat toto řešení, které jim může ulpět v hlavě ... a pak zapláčou, jako kdysi já.
A jistě, lze namítnout, že v případě potřeby je možno kód refaktorovat. Vlastně to při vývoji her považuji za naprostou nutnost - ale to už je jen osobní názor, někteří lidi z oboru s tím nesouhlasí. Jenže jestliže beru v potaz cílení na začátečníky - při překročení určité složitosti toho prostě nejsou schopni. Buď projekt doklepou s vachrlatými základy, které mají, nebo začnou znovu od nuly. Opět vzpomínám na své dávné zkušenosti a tehdejší neschopnost dotáhnout hru do konce.
GC v Go nic nezaseká, má pauzy pod 1 ms. Ideální by sice bylo nula alokací na haldě (jako v případě SW pro vesmírné lety :-), nicméně v praxi funguje escape analýza v Go poměrně dobře a počet alokací na haldě dost redukuje (rozhodně není dokonalá, ale GC problém nebude ani při tisíci objektů na haldě). Přesto mi ale nepřijde jako dobrá volba psát hry v Go, byť jistě to jde (stejně jako v Prologu).
Není, ale ve většině případů (99%) je pod 1 ms. Limit, který runtime prakticky nikdy nepřekročí, je 10 ms. Ovšem pozor, tisíce větších řetězců jsou hračka, v takovém případě jsou typické pauzy v řádu desítek μs. GC v Go se přestává flákat až u hald s velikostí v řádu desítek GB, což asi není úplně typické, určitě ne u her, které běhaly na osmibitech s 16 kB paměti.
Go ve skutečnosti vůbec neznám takže nemohu nic tvrdit na jisto. Nicméně pokud jeho gc nedokáže běžet paralelně spolu s hlavním kódem aplikace nebo pokud gc nemá (alespoň téměř) konstantní časovou složitost, potom tvrzení „má pauzy pod 1 ms“ moc nevěřím. Netvrdím, že tomu tak není, jen se mi to zdá být nepravděpodobné (a to i třeba ve světle tohoto článku). Věřím, že tomu tak může být u hromady běžných aplikací, menších a třeba středních her, ale pokud není splněný alespoň jeden předpoklad výše, tak zde musí být limit. A velké hry ten limit velice rády zkouší. Nicméně pokud by byl Go-GC řádově cca stokrát rychlejší, než .net-GC, pak uznávám, že i pro větší hry by nejspíš nemusel být problémem.
P.S. U Go by u větších her byl problém spíše s tím, že je celkově o něco pomalejší než C(++), ale GC ve verzi Go 1.14 určitě ne. Existují různé limitní případy, na které člověk narazí, když hodně “blbne”, ale jinak jak jsem psal výše, některé firmy mají v aplikacích v Go na haldě třeba 50 GB a latence zůstává nízká (o tom byl někde hezký článek, jak autoři nějaké nenažrané aplikace v Go 1.2-1.4 bombardovali tvůrce jazyka stížnostmi na latence v řádu sekund tak dlouho, až celý GC přepsali s důrazem na minimální latenci — v nedávném porovnání s GC v Javě Go Javu zašlapalo do země :).
Já to taky nemůžu na základě vlastních zkušeností tvrdit na 100%, tak velké haldy nepoužívám a dočasných objektů max. desetitisíce napříč vlákny na serverech. Je to tvrzení tvůrců Go, ovšem podle informací uživatelů (firem) pravdivé. Zvlášť to srovnání s Javou je zajímavé čtení. Člověk ovšem nesmí používat finalizéry, to je pak cesta do pekel.
BTW ten článek o přechodu na Rust se dost probíral, kromě použití staré verze Go (odtud ony CPU “spikes”) to prostě měli blbě napsané. To ovšem nic nemění na faktu, že v Rustu bude správa paměti vždy výkonnější.
K tomu Go - ano urcite to neni jazyk pro ackove tituly. Ale pro kluky, kteri si chteji neco vyzkouset to IMHO neni spatne reseni. Muzou totiz zkusit i Pygame nebo Pyglet a tam jsme s vykonem prakticky o rad jinde :) Nebo si zkusit neco napsat v JS, ale to je tema, kteremu se z mnoha duvodu nechci venovat :)
Na druhou stranu - zkusit si nastrukturovat nejakou hru s mnoha NPC s gorutinami by mohlo byt zajimave. Nezkousel jsem to, ale pouzit gorutiny de facto ve funkci korutin by to mohlo zprehlednit (nebo taky totalne zasekat).
Super, zaprvé moc díky za feedback, který je k věci.
Tu první část jsem myslel trošku jinak, ale asi to vyznělo špatně. Já totiž nezmínil úplný kontext a ten je tento: v dnešní trošku zmatené době se mi ozvali kluci, co sice umí (teoreticky) programovat, protože se to učili ve škole, ale chtějí si kromě nějakého bubble sortu a podobných textových věcí vyzkoušet i nějakou jednoduchou hru. Což je skvělé, že se jen neválí doma (tedy oni se asi doma válí, ale u IDEčka :-).
No a z praxe vím, že je fakt největší problém s udržením stavu aplikace - to je IMHO mnohem důležitější věc na zmínění (při výuce), než důraz na řešení konkrétních algoritmů. Protože ty algoritmy člověk dříve či později nějak odladí, ale to bývá malá a izolovaná věc, kdežto stav se ve větší aplikaci "rozleze" všude a těžko se řídí. S tím GC to asi bylo napsáno nešťastně, protože v kontextu her (a to nemluvíme o áčkových titulech, ale o jednodušších věcech typu tower defense) je to pochopitelně problém, jak sám píšete - ovšem v Go ne tak dobře řešitelný jako v C/C++.
Pozn: ono se "díky" rychlému GC v Go a taky toho, že má alokaci udělanou chytřeji, než v některých jiných GC-based jazycích může zdát GC malým problémem, ale to samozřejmě jen čeká na horší situaci, kdy se může projevit.
Druhá část - ona to není poslední verze, ale opět máte pravdu, měl jsem tam dohodit i (prozatím) poslední slušnější řešení. Upravím to ASAP.
[posilam znovu, predchozi prispevek z nejakeho duvodu ceka na schvaleni]
Super, zaprvé moc díky za feedback, který je k věci.
Tu první část jsem myslel trošku jinak, ale asi to vyznělo špatně. Já totiž nezmínil úplný kontext a ten je tento: v dnešní trošku zmatené době se mi ozvali kluci, co sice umí (teoreticky) programovat, protože se to učili ve škole, ale chtějí si kromě nějakého bubble sortu a podobných textových věcí vyzkoušet i nějakou jednoduchou hru. Což je skvělé, že se jen neválí doma (tedy oni se asi doma válí, ale u IDEčka :-).
No a z praxe vím, že je fakt největší problém s udržením stavu aplikace - to je IMHO mnohem důležitější věc na zmínění (při výuce), než důraz na řešení konkrétních algoritmů. Protože ty algoritmy člověk dříve či později nějak odladí, ale to bývá malá a izolovaná věc, kdežto stav se ve větší aplikaci "rozleze" všude a těžko se řídí. S tím GC to asi bylo napsáno nešťastně, protože v kontextu her (a to nemluvíme o áčkových titulech, ale o jednodušších věcech typu tower defense) je to pochopitelně problém, jak sám píšete - ovšem v Go ne tak dobře řešitelný jako v C/C++.
Pozn: ono se "díky" rychlému GC v Go a taky toho, že má alokaci udělanou chytřeji, než v některých jiných GC-based jazycích může zdát GC malým problémem, ale to samozřejmě jen čeká na horší situaci, kdy se může projevit.
Druhá část - ona to není poslední verze, ale opět máte pravdu, měl jsem tam dohodit i (prozatím) poslední slušnější řešení. Upravím to ASAP.
„moc díky za feedback“
Není zač, konečně máte článek, ke kterému dokáži i vygenerovat relevantní zpětnou vazbu :D.
První část (ohledně GC): jasně, proto jsem i zmínil „Předpokládám, že to tak nebylo myšleno [...]“. S tím, co píšete, naprosto souhlasím. Pointa byla opravdu jen o tom, že vyznění mohlo být trochu zavádějící, než byl úmysl.
„Druhá část - ona to není poslední verze, [...]“
To je mi celkem jasné, jinak bych měl víc poznámek - např. o tom, že hra není framerate-independent, ale přijde mi zjevné, že to není záběrem dnešního článku, který by byl už příliš zahlcující (a tipuji si, že se tomu nejspíš bude věnovat budoucí článek).
Opraveno, jak v clanku, tak i v https://github.com/tisnik/go-root/blob/master/article_63/test10.go
Jeste jednou dik za feedback!
> To, že neřešíme životnost krátkodobých objektů -> Musí být naprosto jasné a deterministické, kdy jsou potřeba a kdy už ne.
s timto jednoznacne souhlasim! (ten koment s GC je napsan velmi trefne), ad zkusenosti s vyvojem hry Planet Gula (v jave, coz ma GC), pro zajemce link -- https://www.youtube.com/channel/UCEpamceiLz1OK9yHZQZVU8A
Mezi GC jsou rozdily i mezi alokaci pameti. V Go je to reseno jinak nez v Jave, protoze se nenechali omezit "standardem" datovanym nekdy od dob Pascalu - ze lokalni promenne jsou na zasobnikovem ramci a dynamicky alokovane na halde. V Go je to tak i tak podle analyzy kodu, takze tam v dusledku jsou GC pauzy skutecne male. GC se stop-the-world chovanim to neni - viz blogy o tom, jak a proc GC navrhovali.
Tedy - je GC a GC, nehazme je do jednoho pytle.