OPTIMALIZÁCIA:
Tím WAWOTpočasprácena tímovom projekte vykonal nasledujúce optimalizácie:
Na základe analýzy pomocou nástroja Yourkit Java Profiler boli identifikované kusy kódu, ktoré sú v riešení výpočtovo najnáročnejšie. Identifikované metódy bolo potom možné upraviť, aby sa nevykonávali časti, ktoré nie sú pri niektorých úlohách nevyhnutné.
Konkrétne ide o zmeny v metódach WorldModel.processNewServerMessage(ParsedData) a AgentModel.processNewServerMessage(ParsedData), ktorých pôvodnú zložitosť vidno na obr. č. 1. Je to 50% resp. 38%.
Po analyzovaní kódu týchto metód sa ukázalo, že vytvárajú dáta typu String aj v prípadoch, kedy sa tieto dáta ďalej nepoužijú. V prvom prípade vytvorenie správy, ktorá sa pošle do TestFrameworku a v druhom vytvorenie logového výpisu.
V tomto prípade sa v triede WorldModelzbytočne vytvára objekt typu Message, kde sa pre vytvorenie používa ObjectOutputStream, ktorý zaberá dosť času. Táto správa sa následne pošle do metódy na odoslanie do TF, kde sa ale odoslanie vykoná iba v prípade, že je TF zapnutý. Ide konkrétne oif (Settings.getBoolean(“TestFramework_monitor_enable”)). Aby sme zabránili zbytočnému vytváraniu správy, tento if sme dali rovnako aj pred vytváranie správy a následné volanie metódy na odoslanie. Funkcionalita odosielania správ do TF by teda mala byť zachovaná, ale podarilo sa nám skresať náročnosť o značnú časť, čo vyplýva z obr. č. 2.
V rámci tejto metódy sa volajú zmeny v chovaní a stave hráča na základe spracovaných dát zo servera. Konkrétne v metóde updateBodyPartsPositions2(),ktorá je počasspracovávania volaná sa nachádza vypisovanie do logu o stave hráča. Tento výpis sa zapína v GUI pomocou tlačidla AGENT_MODEL. Aj keď je tento výpis vypnutý, správa sa vytvára pomocou StringBuilder-u, čo predstavuje značné spomalenie. Aby sme sa zbytočnému vytváraniu správy vyhli, bol do triedy Settingspridaný atribút „logBodyPartsPosition“, ktorý určuje, či sa má logovanie použiť. V kóde je potom podmienka, kde sa kontroluje, či má daný atribút hodnotu true/false a až potom je samotný výpis do logu. Nastavenie atribútu sa deje defaultne pri spustení programu, kedy sa načíta uložená konfigurácia logovania a prepína sa pri vypnutí/zapnutí atribútu na logovanie AGENT_MODEL v GUI. Touto zmenou sme dosiahli ďalšie ušetrenie výkonu CPU.
Týmito zmenami sme ušetrili čas, ktorý bol zbytočne zabratý časťami kódu, ktoré nie je potrebné vykonávať vždy. Avšak v prípade, že tieto konštrukcie sú potrebné, naďalej sa budú v programe vykonávať. V tomto prípade ale dané metódy optimalizovať nevieme pri zachovaní pôvodnej funkcionality.
Cieľom úlohy bolo optimalizovanie triedy sk.jim.fiit.agent.parsing.Parser na základe analýzy Yourkitom, ktorá ukázala, že niektoré metódy v nej sú pomerne výpočtovo náročné. V tejto triede sa nachádza spracovanie komunikačných správ a následné volanie príslušných objektov, ktoré na správy reagujú.
Optimalizovanie už bolo dokončené v rámci úloh v predošlom šprinte, kde boli vypnuté niektoré volania odosielania správ a výpisov do logu. Iné vylepšenia tejto triedy sa neidentifikovali.
Cieľom úlohy bolo optimalizovanie triedy sk.fiit.jim.agent.skills na základe analýzy Yourkitom, ktorá prvotne ukázala, že niektoré metódy v nej sú pomerne výpočtovo náročné. V tejto triede sa nachádza výber high skillu hráča na základe aktuálnej vybranej taktiky a stavu. Nenachádza sa tu však nič, čo by sa dalo optimalizovať, keďže nejde o žiadne výpočty.
V hlavnej metóde triedy je už takisto použitý switch, čo je pravdepodobne z dôvodu, že trieda už niekedy optimalizovaná bola. Opätovné spustenie analýzy v Yourkite tiež neukázalo zložitosť tejto triedy.
Cieľom úlohy je optimalizovanie triedy sk.fiit.jim.agent.models.AgentModelna základe analýzy Yourkitom, ktorá prvotne ukázala, že niektoré metódy v nej sú pomerne výpočtovo náročné. V tejto triede sa nachádza aktualizovanie stavu hráča, nastavenia jeho častí tela a polohy. Väčšinou ide o vektorové a podobné výpočty, ktoré sa viac optimalizovať zrejme nedajú. V triede sa ale nachádza mnoho výpisov do logov, ktoré sa volajú aj keď nie sú zapnuté. Časť týchto výpisov už bola vypnutá na základe flagu v rámci úlohy v minulom šprinte (metóda updateBodyPartsPositions2()), ale niektoré výpisy, ktoré tiež zaberali nepatrný čas ostali zapnuté. Sú to výpisy v metódach updateCenterOfMass() a updateZeroMomentPoint(). V rámci úlohy boli tieto výpisy takisto vypnuté, takže sa ušetrilo niekoľko percent z celkového času vykonávania.
Vypnutie logov, keď ich nie je potreba ušetrilo ~2% času. Rozdiel je viditeľný na porovnaní obr. č. 1 a obr. č. 2. Funkcionalita, keď sú logy zapnuté ostáva nezmenená.
Trieda sk.fiit.jim.agent.movesje podľa Yourkit nástroja výpočtovo náročnejší (~.moves.LowSkills.step()ma hodnotu 7%) preto je potrebné sa na to pozrieť pre prípadné zoptimalizovanie zdrojového kódu.
Cieľom tejto úlohy bolo optimalizovanie zdrojových kódov tried nachádzajúcich sa v priečinku sk.fiit.jim.agent.movesna základe analýzy nástroja Yourkit. Tento nástroj ukázal, že niektoré metódy v triedach sú pomerne výpočtovo náročné. V týchto triedach sa nachádza výber low skill-u hráča na základe aktuálnej vybranej taktiky a stavu. Zistili sme, že v triede LowSkillsa nachádzajú Log výpisy, ktoré po ošetrení podmienkou if(Setting.getBoolean(„lowSkillsLogger“))sa znížila výpočtová náročnosť tejto triedy na 0%. Log výpisy sa odstránili v metódach step()a setNewActionPhase(). V triede SkipFlag sa upravil zdrojový kód v metóde equals()na základe odporúčania prostredia InteliJ. Táto úprava zahŕňala zjednodušenie podmienok IF.
V metóde NewActivePhase()triedy LowSkillsa nachádza komentár:
“TODO possible stack overflow if all phases have the same skipIf”
Flag Žiaľ tomuto komentáru sme neporozumeli pretože premenná skipIfFlag sa v kóde nenachádza a aktuálny stav zdrojové kódu nezvyšuje výpočtovú náročnosť. Na základe analýzy sa nám podarilo znížiť výpočtovú náročnosť agenta Jim o 7%, konkrétne v triede LowSkill v metódach step() a setNewActionPhase(). Výsledok optimalizácie môžete vidieť na obrázku nižšie.
Cieľom bolo pozrieť sa a analyzovať, čo sa dá vylepšiť v rámci gemeometrických výpočtov v agentovi. Tieto výpočty sa nachádzajú v balíku sk.fiit.robocup.library.geometry.
Trieda Angles
V tejto triede sa nevykonávajú žiadne výpočty okrem prevádzania uhlov v stupňoch na radiány, normalizujú sa uhly, prípadne sa počítajú rozdiely medzi uhlami. V rámci optimalizácie som nenašiel nič čo by mohlo vylepšiť časovú zložitosť.
Trieda Circle
V tejto triede neprebiehajú žiadne výpočty, iba sa nastavuje kruh pomocou stredu a polomeru, a tieto hodnoty sa pomocou get metód dajú naspať získať. Nie je tu nič na optimalizáciu.
Trieda Line2D
V tejto triede sa na základe dvoch bodov alebo jedného bodu a smerového vektoru dopočíta normálový vektor, smerový vektor alebo druhý bod a rovnica čiary ax+ by + c = 0. Čo sa týka výpočtov prebieha tu ešte počítanie prieniku čiary a inej čiary, a taktiež čiary a kružnice. Avšak ani tu som nenašiel priestor na optimalizáciu
Trieda MEC
V tejto triede sa počíta minimálna kružnica, ktorá obsahuje všetky body vstupného listu. Počíta sa pre 1, 2 alebo 3 body. Testovaním som zistil, že táto trieda sa zrejme vôbec nepoužíva.
Trieda ParsedLinesWithFlags
V tejto triede jediné výpočty ktoré prebiehajú sú jednoduché if-y (zisťovanie či je uhol v rozmedzí -58 až 58), počítanie vzdialeností medzi dvoma vektormi (čiarami). Nenašiel som priestor na optimalizáciu.
Trieda Point2D
V tejto triede je reprezentovaný 2D bod. Výpočty ktoré prebiehajú sú interpolácia medzi 2 bodmi,a zisťovanie vzdialeností dvoch bodov. Nenašiel som priestor na optimalizáciu.
Trieda Point3D
V tejto triede nie sú žiadne výpočty, iba konštruktor na vytvorenie 3D bodu a metódy get na získanie jednotlivých súradníc.
Trieda Vector2D
V tejto triede je reprezentovaný dvojrozmerný vektor, prebiehajú tu výpočty veľkosti uhla medzi dvoma vektormi a zisťovanie či sú dva vektory totožné (je tu miera 0.01 v rozdiely hodnôt x aj y).
Trieda Vector3D
V tejto triede sa nachádzajú 2 metódy, ktoré podľa yourkitu zaberajú významný čas počasbehu programu. caretsian(double,double,double) calculateSpherical() V metóde cartesian(double,double,double) sú len jednoduché priradenia (vytvorenie nového vektoru a naplnenie hodnotami) a volanie metódy calculateSpherical(). V metóde calculateSpherical sa z karteziánskych súradníc x,y,z počítajú uhly theta a phi, ktoré sa následne normalizujú. Takže ani v tejto triede som nenašiel priestor na optimalizáciu.
Aj napriek tomu, že som v týchto triedach nenašiel priestor na časovú optimalizáciu, niektoré triedy by bolo možné upraviť aby boli lepšie čitateľnejšie a spĺňali konvencie definované v metodike.
Bolo implementované vypnutie posielania správ do testframeworku a vypnuté logovanie.
Testovacie scenáre boli spustené a overené.
Oba testovacie prípady boli na 100% úspešné. Implementácia zmeny posielania správ bola otestovaná.
Testy boli na 100% úspešné.
Doposiaľ boli podnety zo servera prijímané metódoureceive() v triede Communication.java a následne ďalej vyhodnocované v triede Parser.java. Spracovanie správ spotrebuje nezanedbateľnú časť zdrojov a oddelili sme ho preto do samostatného celku, ktorý je možné spustiť ako vlákno. Výsledky tejto snahy sú opísané v nasledujúcej časti tohto dokumentu.
Trieda Communication.java
K úpravám, ktoré stoja za zmienku, patrí implementácia rozhraniaRunnable a metódy run(). Otvorenie spojenia bolo extrahované do metódy, ktorá je volaná len raz počas inicializácie programu.
Trieda Parser.java
Správa sa preposiela prihláseným pozorovateľom (observerom) v metódenotifyObservers(). Táto časť kódu tvorí tzv. kritickú oblasť, nakoľko ostatné vlákna musia čakať, kým je správa od jedného vlákna odoslaná a spracovaná všetkými pozorovateľmi. Do tejto časti kódu má preto prístup vždy len jedno vlákno, zatiaľ čo ostatné čakajú na uvoľnenie prostriedku. Túto reguláciu zabezpečuje mechanizmus, ktorý sa volá monitor a v Jave je implementovaný ako ReentrantLock.
Náš prvý pokus výrazne zrýchlil beh programu, ale spôsobil časté pády hráča. Tabuľka 1 zobrazuje 10 meraní výpočtovej náročnosti pred a po zmene implementácie. Celkovo sa zvýšila rýchlosť o 9,7%.
Je potrebné podotknúť, že s implementáciou tejto úlohy sa začalo až ku koncu semestra a nemali sme dostatok času na podrobné porovnanie s predchádzajúcou verziou. Tento problém bude predmetom ďalšieho výskumu v úlohe 10836.
Prvou podstatnou časťou bolo zistiť prečo sa vôbec Jim do tohto stavu dostane a aký lowskill by bolo najlepšie v danej situácii využiť. S dlhodobého skúmania situácie Jima a dôvodu prečo sa vôbec začne vykonávať tento zdraviu užitočný pohyb sme zistili, že najvhodnejšie bude namiesto priameho LowSkillu vykonávať HighSkill Localize. Tento skill Jimovi umožní obnoviť svoje znalosti o lokácii a pomôže mu so svižným návratom do hry. Na toto som musel však upraviť/pridať viaceré xml súbory a opraviť 2 triedy.
Ovplyvnené xml súbory sú head_down.xml, head_left.xml, head_right.xml a head_left_and_right.xml.
Ovplyvnené boli tieto triedy:
Walk.java – táto trieda obsahovala rozhodovací strom v ktorom san a konci dostal Jim k pokynu vykonávania spartakiády. Toto bolo napravené pridaním HighSkillu “Localize” do zoznamu vykonávaných HighSkillov na začiatok a následné vyvolanie LowSkillu “rollback”.
Localize.java – tento HighSkill bol do vysokej miery upravený – nastalo pridanie možnosti pozrieť sa dole. Taktiež bola upravená rozhodovacia štruktúra. Pridaním rozhodnutia o pozrení dole bolo pre efektívnosť vhodné pridať metódu, ktorá resetuje histótiu vykonania pohybov hlavou pri zmene polohy.