{"id":879,"date":"2011-04-13T22:15:50","date_gmt":"2011-04-13T21:15:50","guid":{"rendered":"http:\/\/netrix.org.pl\/?p=879"},"modified":"2011-04-13T22:15:50","modified_gmt":"2011-04-13T21:15:50","slug":"optymalizacja-aplikacji-c-z-wykorzystaniem-amd-codeanalyst","status":"publish","type":"post","link":"https:\/\/netrix.org.pl\/index.php\/2011\/04\/13\/optymalizacja-aplikacji-c-z-wykorzystaniem-amd-codeanalyst\/","title":{"rendered":"Optymalizacja aplikacji C++ z wykorzystaniem AMD CodeAnalyst"},"content":{"rendered":"<p>W tej notce opisz\u0119 moj\u0105 pierwsz\u0105 porz\u0105dn\u0105 styczno\u015b\u0107 z programem CodeAnalyst i przy okazji poka\u017c\u0119, w jaki spos\u00f3b uda\u0142o mi si\u0119 nieco zoptymalizowa\u0107 napisan\u0105 przeze mnie aplikacj\u0119 implementuj\u0105c\u0105 rozwi\u0105zywanie problemu kolorowania grafu za pomoc\u0105 algorytmu genetycznego.<\/p>\n<p><a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/code_analyst.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-medium wp-image-885\" title=\"code_analyst\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/code_analyst-300x215.png\" alt=\"CodeAnalyst - Okno g\u0142\u00f3wne programu\" width=\"198\" height=\"141\" srcset=\"https:\/\/netrix.org.pl\/wp-content\/2011\/04\/code_analyst-300x215.png 300w, https:\/\/netrix.org.pl\/wp-content\/2011\/04\/code_analyst.png 771w\" sizes=\"(max-width: 198px) 100vw, 198px\" \/><\/a>CodeAnalyst jest programem s\u0142u\u017c\u0105cym do profilowania aplikacji. Jego zadaniem jest zbieranie r\u00f3\u017cnego rodzaju pr\u00f3bek w trakcie dzia\u0142ania testowanej aplikacji, kt\u00f3re s\u0105 nast\u0119pnie przedstawiane w formie wykresu lub tabeli. Dzi\u0119ki temu wida\u0107 od razu, kt\u00f3re miejsca aplikacji maj\u0105 najwi\u0119kszy wp\u0142yw na badany parametr z dok\u0142adno\u015bci\u0105 do wykonywanych instrukcji Assemblera. Program ten mo\u017cna znale\u017a\u0107 na <a href=\"http:\/\/developer.amd.com\/cpu\/codeanalyst\/Pages\/default.aspx\"><strong>stronie producenta<\/strong><\/a>. Jego g\u0142\u00f3wne okienko jest przedstawione na obrazku obok.<\/p>\n<p>Jak ju\u017c wspomnia\u0142em badan\u0105 aplikacj\u0105 jest moja implementacja algorytmu ewolucyjnego u\u017cytego do rozwi\u0105zania problemu kolorowania grafu. Kod tej aplikacji wraz z przyk\u0142adowym grafem znajduje si\u0119 <a href=\"http:\/\/netrix.org.pl\/content\/blog\/ea_gcp_src.zip\"><strong>tutaj<\/strong><\/a>. Jest to kod, od kt\u00f3rego wychodz\u0119 w tej notce, zatem nie uwzgl\u0119dnia on \u017cadnych poprawek. Poni\u017csza tabela zawiera \u015brednie wyniki pomiaru cykli i czasu jakie zabiera g\u0142\u00f3wna p\u0119tla programu na dw\u00f3ch maszynach:<\/p>\n<table style=\"margin-bottom: 10px; width: 100%; border-collapse: collapse; border-width: 1px; border-style: inset\">\n<tr>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Procesor<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">System<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Kompilator<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Cz\u0119stotliwo\u015b\u0107 zegara<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Liczba cykli<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Czas<\/th>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">AMD Turion64 2 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">MS Windows 7<\/td>\n<td style=\"border-width: 1px; border-style: inset\">msvc<\/td>\n<td style=\"border-width: 1px; border-style: inset\">25 MHz (QPF)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">4111926149 cykli (QPC)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">164,48 s<\/td>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">Intel Pentium 4 2,66 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">Linux<\/td>\n<td style=\"border-width: 1px; border-style: inset\">g++<\/td>\n<td style=\"border-width: 1px; border-style: inset\">2659,98 MHz (cpuinfo)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">268359191768 cykli (__rdtsc())<\/td>\n<td style=\"border-width: 1px; border-style: inset\">100,89 s<\/td>\n<\/tr>\n<\/table>\n<p>Optymalizacja b\u0119dzie dotyczy\u0107 g\u0142\u00f3wnie tej pierwszej platformy, ale postanowi\u0142em z ciekawo\u015bci sprawdzi\u0107 jak zmiany b\u0119d\u0105 wp\u0142ywa\u0107 na czas wykonywania si\u0119 kodu r\u00f3wnie\u017c na drugiej platformie.<\/p>\n<p>Analiza programu b\u0119dzie polega\u0107 na szukaniu miejsc, w kt\u00f3rych sp\u0119dza on najwi\u0119cej czasu. Zatem na pocz\u0105tek nale\u017cy uruchomi\u0107 program CodeAnalyst, a nast\u0119pnie utworzy\u0107 projekt wybieraj\u0105c profil <em>Time-based profile<\/em>. Wed\u0142ug mnie dobrze jest r\u00f3wnie\u017c zaznaczy\u0107 opcje <em>Stop data collection when app exits<\/em> oraz <em>Profile the duration of the app execution<\/em>. Dzi\u0119ki temu CodeAnalyst poczeka z analiz\u0105 wynik\u00f3w a\u017c badana aplikacja sko\u0144czy dzia\u0142anie. Po utworzeniu projektu mo\u017cna przyst\u0105pi\u0107 do test\u00f3w. Zatem po wyciszeniu\/wy\u0142\u0105czeniu niepotrzebnych aplikacji mo\u017cna wybra\u0107 z menu <em>Profile -&gt; Start<\/em> lub klikn\u0105\u0107 zielon\u0105 strza\u0142k\u0119.<\/p>\n<p>Po zako\u0144czeniu dzia\u0142ania aplikacji zostanie wy\u015bwietlone podsumowanie, kt\u00f3re wygl\u0105da mniej wi\u0119cej tak:<br \/>\n<a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemdata.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-892\" title=\"ca_systemdata\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemdata-300x215.png\" alt=\"CodeAnalyst - System data\" width=\"198\" height=\"141\" srcset=\"https:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemdata-300x215.png 300w, https:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemdata.png 771w\" sizes=\"(max-width: 198px) 100vw, 198px\" \/><\/a><a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemgraph.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-893\" title=\"ca_systemgraph\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemgraph-300x215.png\" alt=\"CodeAnalyst - System graph\" width=\"198\" height=\"141\" srcset=\"https:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemgraph-300x215.png 300w, https:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_systemgraph.png 771w\" sizes=\"(max-width: 198px) 100vw, 198px\" \/><\/a><\/p>\n<p>Obie te zak\u0142adki przedstawiaj\u0105 dok\u0142adnie to samo, czyli procentowy udzia\u0142 pr\u00f3bek w ka\u017cdej aplikacji\/bibliotece\/module, r\u00f3\u017cnica wyst\u0119puje tylko w formie przedstawienia danych. Co w tym podsumowaniu jest interesuj\u0105cego? W tym widoku jeszcze nic, ale CodeAnalyst umo\u017cliwia podgl\u0105d szczeg\u00f3\u0142\u00f3w ka\u017cdego z wymienionych wcze\u015bniej byt\u00f3w, nawet dowolnego procesu wybranego z zak\u0142adki <em>Processes<\/em>. Jednak poniewa\u017c ta notka obejmuje tylko jedn\u0105 aplikacje, w dodatku t\u0119, kt\u00f3ra zebra\u0142a najwi\u0119cej pr\u00f3bek, zatem przejd\u0119 teraz do niej. Oto co pokazuje si\u0119 po dwukrotnym klikni\u0119ciu na jej proces:<br \/>\n<a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-898\" title=\"ca_tested_app\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app1-300x215.png\" alt=\"CodeAnalyst - Testowana aplikacja\" width=\"198\" height=\"141\" \/><\/a><\/p>\n<p>W tym widoku wida\u0107 ju\u017c nieco wi\u0119cej. S\u0105 tu pokazane funkcje, kt\u00f3re zabiera\u0142y najwi\u0119cej czasu. Na samej g\u00f3rze wida\u0107 jedn\u0105, kt\u00f3ra zabiera a\u017c 40% pr\u00f3bek, jest to funkcja <em>EvaluateConflicts(<\/em>), kt\u00f3ra zlicza konflikty dla ka\u017cdego osobnika w ca\u0142ej populacji. Warto si\u0119 jej przyjrze\u0107 z bliska:<br \/>\n<a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_evaluateConflicts.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_evaluateConflicts-300x181.png\" alt=\"CodeAnalysy - Podgl\u0105d funkcji EvaluateConflicts()\" title=\"ca_evaluateConflicts\" width=\"198\" height=\"141\" class=\"alignnone size-medium wp-image-924\" \/><\/a><\/p>\n<p>CodeAnalyst, wykorzystuj\u0105c dane zawarte w pliku .pdb, kt\u00f3ry powinien znajdowa\u0107 si\u0119 obok pliku wykonywalnego, potrafi wy\u015bwietli\u0107 i dopasowa\u0107 pr\u00f3bki do odpowiedniego miejsca w kodzie. Mo\u017cna r\u00f3wnie\u017c podejrze\u0107 wykonywany w tym miejscu kod Assemblera, co jest bardzo przydatne (\u017ceby nie powiedzie\u0107 kluczowe) przy profilowaniu aplikacji. Co zatem wida\u0107 na powy\u017cszym screenie? Jest tam co\u015b co tak naprawd\u0119 powinno znajdowa\u0107 si\u0119 tylko i wy\u0142\u0105cznie w aplikacji kompilowanej w trybie Debug. To sprawdzanie poprawno\u015bci odwo\u0142ania si\u0119 do elementu tablicy w klasie <em>std::vector<\/em>. Jest to nieco dziwne, poniewa\u017c jest tam u\u017cywany <em>operator[]<\/em>, a standardowo ta wersja odwo\u0142ania nie powinna by\u0107 sprawdzana, w przeciwie\u0144stwie do funkcji <em>at()<\/em>.<br \/>\nOkazuje si\u0119 jednak, \u017ce implementacja STL dostarczana z kompilatorem <em>msvc<\/em> ma zaimplementowane takie sprawdzenie, kt\u00f3re mo\u017cna co prawda wy\u0142\u0105czy\u0107 flag\u0105 <em>_SECURE_SCL<\/em> ustawion\u0105 na 0, ale domy\u015blnie ta opcja jest w\u0142\u0105czona. Wi\u0119cej informacji mo\u017cna znale\u017a\u0107 <a href=\"http:\/\/stackoverflow.com\/questions\/1290396\/how-to-make-stdvectors-operator-compile-doing-bounds-checking-in-debug-but-n\"><strong>tu<\/strong><\/a> lub bezpo\u015brednio <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa985965%28v=VS.80%29.aspx\"><strong>tutaj<\/strong><\/a>.<\/p>\n<p>Poni\u017csza tabela zawiera wyniki pomiar\u00f3w cykli i czasu z wy\u0142\u0105czeniem tego sprawdzania:<\/p>\n<table style=\"margin-bottom: 10px; width: 100%; border-collapse: collapse; border-width: 1px; border-style: inset\">\n<tr>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Procesor<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">System<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Kompilator<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Cz\u0119stotliwo\u015b\u0107 zegara<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Liczba cykli<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Czas<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Zysk<\/th>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">AMD Turion64 2 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">MS Windows 7<\/td>\n<td style=\"border-width: 1px; border-style: inset\">msvc<\/td>\n<td style=\"border-width: 1px; border-style: inset\">25 MHz (QPF)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">3191994350 cykli (QPC)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">127,68 s<\/td>\n<td style=\"border-width: 1px; border-style: inset\">22,37%<\/td>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">Intel Pentium 4 2,66 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">Linux<\/td>\n<td style=\"border-width: 1px; border-style: inset\">g++<\/td>\n<td style=\"border-width: 1px; border-style: inset\">2659,98 MHz (cpuinfo)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">269220460226 cykli (__rdtsc())<\/td>\n<td style=\"border-width: 1px; border-style: inset\">101,21 s<\/td>\n<td style=\"border-width: 1px; border-style: inset\">-0.32%<\/td>\n<\/tr>\n<\/table>\n<p>W przypadku <em>msvc<\/em> wida\u0107, \u017ce jest ju\u017c lepiej, a przypadek <em>gcc<\/em> zosta\u0142 praktycznie nietkni\u0119ty, zatem nie posiada on domy\u015blnie tego typu sprawdzania. Na poni\u017cszych screenach widniej\u0105 wyniki ponownego pr\u00f3bkowania aplikacji.<br \/>\n<a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app2-300x181.png\" alt=\"CodeAnalysy - Pr\u00f3bki w programie po pierwszej poprawce\" title=\"ca_tested_app2\" width=\"198\" height=\"141\" class=\"alignnone size-medium wp-image-939\" \/><\/a><a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_evaluateConflicts2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_evaluateConflicts2-300x181.png\" alt=\"CodeAnalyst - Funkcja EvaluateConflicts po wprowadzeniu pierwszych poprawek\" title=\"ca_evaluateConflicts2\" width=\"198\" height=\"141\" class=\"alignnone size-medium wp-image-940\" \/><\/a><\/p>\n<p>Jak mo\u017cna zauwa\u017cy\u0107, ta drobna poprawka pozwoli\u0142a kompilatorowi zinline&#8217;owa\u0107 ca\u0142\u0105 funkcj\u0119 EvaluateConflicts(). Dzi\u0119ki temu aplikacja wykonuje si\u0119 szybciej o ok. 22 %. Kod tej wersji znajduje si\u0119 pod <a href=\"http:\/\/netrix.org.pl\/content\/blog\/ea_gcp_src2.zip\"><strong>tym<\/strong><\/a> linkiem.<\/p>\n<p>Jednak na tym nie koniec poprawek, poniewa\u017c jest jeszcze jedno miejsce, z kt\u00f3rym da si\u0119 co\u015b zrobi\u0107. Ot\u00f3\u017c je\u015bli si\u0119 przyjrze\u0107 podsumowaniu ca\u0142ej aplikacji jeszcze raz, mo\u017cna zauwa\u017cy\u0107, \u017ce sp\u0119dza ona du\u017co czasu w funkcji <em>insert()<\/em> drzewa <em>std::_Tree<\/em>, kt\u00f3re jest implementacj\u0105 zbioru (<em>std::set<\/em>) w bibliotece standardowej. Klasa ta jest u\u017cywana w operatorze selekcji w celu wybrania losowych i niepowtarzaj\u0105cych si\u0119 osobnik\u00f3w do turnieju. Co za tym idzie, funkcja <em>insert()<\/em> jest wywo\u0142ywana minimum raz dla ka\u017cdego wolnego miejsca w turnieju (w przypadku testowym jest 50 miejsc), a\u017c do wype\u0142nienia ca\u0142ej nowej populacji (tutaj 5000), czyli kr\u00f3tko m\u00f3wi\u0105c &#8211; do\u015b\u0107 cz\u0119sto.<\/p>\n<p>Rozwi\u0105zaniem, kt\u00f3re przyj\u0105\u0142em w tym miejscu by\u0142o ca\u0142kowite pozbycie si\u0119 klasy <em>std::set<\/em> na rzecz w\u0142asnego sposobu wybierania losowych i r\u00f3\u017cnych osobnik\u00f3w do turnieju. Wynikiem tego jest taka oto klasa:<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\ntemplate<uint RANGE>\r\nclass RandomSet    \/\/ singleton\r\n{\r\npublic:\r\n    RandomSet()\r\n    { \r\n        for(uint i = 0; i < RANGE; ++i)\r\n        { m_aSet[i] = i; }\r\n    }\r\n\r\n    const uint* GetRandomSet(uint uCount, uint uInnerRange)\r\n    {\r\n        uint uTemp, uRand;\r\n        for(uint i = 0; i < uCount; ++i)\r\n        {\r\n            uRand = rand() % (uInnerRange - i);\r\n            uTemp = m_aSet[i];\r\n            m_aSet[i] = m_aSet[i + uRand];\r\n            m_aSet[i + uRand] = uTemp;\r\n        }\r\n        return m_aSet;\r\n    }\r\n    \r\nprivate:\r\n    uint m_aSet[RANGE];\r\n};\r\n<\/pre>\n<p>Klasa ta wykorzystuje tablic\u0119, w kt\u00f3rej zapisane s\u0105 kolejno indeksy wszystkich osobnik\u00f3w. Przy ka\u017cdym wywo\u0142aniu funkcji <em>GetRandomSet()<\/em> tablica ta jest ponownie mieszana tak, by na jej pocz\u0105tku znalaz\u0142y si\u0119 w miar\u0119 losowe warto\u015bci. Dzi\u0119ki temu, \u017ce wybierane warto\u015bci s\u0105 zamieniane z warto\u015bciami z pocz\u0105tku tablicy, zbi\u00f3r pozostaje pe\u0142ny ale jest on stopniowo mieszany, przy czym najwi\u0119ksza r\u00f3\u017cnorodno\u015b\u0107 wyst\u0119puje zawsze na pocz\u0105tku tablicy. To ostatnie nie ma tak naprawd\u0119 wi\u0119kszego znaczenia, poniewa\u017c najwa\u017cniejsze jest to, \u017ceby funkcja zwraca\u0142a za ka\u017cdym razem r\u00f3\u017cne warto\u015bci.<\/p>\n<p>Kolejna tabela zawiera wyniki test\u00f3w po wprowadzeniu tych zmian:<\/p>\n<table style=\"margin-bottom: 10px; width: 100%; border-collapse: collapse; border-width: 1px; border-style: inset\">\n<tr>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Procesor<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">System<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Kompilator<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Cz\u0119stotliwo\u015b\u0107 zegara<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Liczba cykli<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Czas<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Zysk<\/th>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">AMD Turion64 2 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">MS Windows 7<\/td>\n<td style=\"border-width: 1px; border-style: inset\">msvc<\/td>\n<td style=\"border-width: 1px; border-style: inset\">25 MHz (QPF)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">1144922576 cykli (QPC)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">45,8 s<\/td>\n<td style=\"border-width: 1px; border-style: inset\">64,13 %<\/td>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">Intel Pentium 4 2,66 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">Linux<\/td>\n<td style=\"border-width: 1px; border-style: inset\">g++<\/td>\n<td style=\"border-width: 1px; border-style: inset\">2659,98 MHz (cpuinfo)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">105596054178 cykli (__rdtsc())<\/td>\n<td style=\"border-width: 1px; border-style: inset\">39,7 s<\/td>\n<td style=\"border-width: 1px; border-style: inset\">60,78 %<\/td>\n<\/tr>\n<\/table>\n<p>W przypadku tej poprawki mo\u017cna ju\u017c zaobserwowa\u0107 znaczny zysk i to na obu platformach (Windows - 64%, Linux - 61 %). Na poni\u017cszych obrazkach kolejne zrzuty z CodeAnalyst.<br \/>\n<a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app3.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app3-300x215.png\" alt=\"CodeAnalyst - Poprawka GetRandomSet - podsumowanie aplikacji\" title=\"ca_tested_app3\" width=\"198\" height=\"141\" class=\"alignnone size-medium wp-image-960\" srcset=\"https:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app3-300x215.png 300w, https:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_tested_app3.png 771w\" sizes=\"(max-width: 198px) 100vw, 198px\" \/><\/a><a href=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_GetRandomSet.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/netrix.org.pl\/wp-content\/2011\/04\/ca_GetRandomSet-300x181.png\" alt=\"CodeAnalyst - Poprawka GetRandomSet - funkcja\" title=\"ca_GetRandomSet\" width=\"198\" height=\"141\" class=\"alignnone size-medium wp-image-961\" \/><\/a><\/p>\n<p>Wida\u0107 tutaj, \u017ce ilo\u015b\u0107 pr\u00f3bkowanego kodu znacznie zmala\u0142a, a proporcje mi\u0119dzy funkcjami zn\u00f3w si\u0119 zr\u00f3\u017cnicowa\u0142y. Dzi\u0119ki temu mo\u017cna zobaczy\u0107, \u017ce funkcja <em>GetRandomSet()<\/em> zabiera znacznie mniej pr\u00f3bek ni\u017c <em>EvaluateGeneration()<\/em>. Kod tej wersji mo\u017cna pobra\u0107 <a href=\"http:\/\/netrix.org.pl\/content\/blog\/ea_gcp_src3.zip\"><strong>st\u0105d<\/strong><\/a>.<\/p>\n<p>Pozostaje teraz ponownie zapyta\u0107, czy mo\u017cna co\u015b tu jeszcze zoptymalizowa\u0107? Mo\u017ce i mo\u017cna, ale prawdopodobnie zajmie to ju\u017c troch\u0119 wi\u0119cej czasu ni\u017c poprzednie poprawki. Jednym z oczywistych krok\u00f3w jakie mo\u017cna tutaj wykona\u0107 to zast\u0105pienie <em>std::vector<\/em> zwyk\u0142ymi tablicami dynamicznymi, ale w tym przypadku zysk jest osi\u0105galny tylko na Linuksie co pokazuje kolejna tabela:<\/p>\n<table style=\"margin-bottom: 10px; width: 100%; border-collapse: collapse; border-width: 1px; border-style: inset\">\n<tr>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Procesor<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">System<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Kompilator<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Cz\u0119stotliwo\u015b\u0107 zegara<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Liczba cykli<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Czas<\/th>\n<th style=\"border-width: 1px; border-style: outset; background-color: #CCCCCC\">Zysk<\/th>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">AMD Turion64 2 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">MS Windows 7<\/td>\n<td style=\"border-width: 1px; border-style: inset\">msvc<\/td>\n<td style=\"border-width: 1px; border-style: inset\">25 MHz (QPF)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">1144922576 cykli (QPC)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">46,2 s<\/td>\n<td style=\"border-width: 1px; border-style: inset\">-0.89 %<\/td>\n<\/tr>\n<tr>\n<td style=\"border-width: 1px; border-style: inset\">Intel Pentium 4 2,66 GHz<\/td>\n<td style=\"border-width: 1px; border-style: inset\">Linux<\/td>\n<td style=\"border-width: 1px; border-style: inset\">g++<\/td>\n<td style=\"border-width: 1px; border-style: inset\">2659,98 MHz (cpuinfo)<\/td>\n<td style=\"border-width: 1px; border-style: inset\">105596054178 cykli (__rdtsc())<\/td>\n<td style=\"border-width: 1px; border-style: inset\">37 s<\/td>\n<td style=\"border-width: 1px; border-style: inset\">6,78 %<\/td>\n<\/tr>\n<\/table>\n<p>Kod tego przypadku znajduje si\u0119 <a href=\"http:\/\/netrix.org.pl\/content\/blog\/ea_gcp_src4.zip\"><strong>tutaj<\/strong><\/a>.<\/p>\n<p>I to w\u0142a\u015bciwie tyle, wnioski? CodeAnalyst jest bardzo dobrym narz\u0119dziem do profilowania aplikacji, jest \u0142atwy w obs\u0142udze, a co najwa\u017cniejsze jest za darmo. Opr\u00f3cz tego warto zwraca\u0107 uwag\u0119 na implementacje biblioteki standardowej w u\u017cywanym kompilatorze, poniewa\u017c mo\u017cna si\u0119 nieco zdziwi\u0107, tak jak to mia\u0142o miejsce w przypadku <em>std::vector<\/em> w tym przyk\u0142adzie. Dodatkowo warto czasami poszuka\u0107 innych rozwi\u0105za\u0144 dla niekt\u00f3rych problem\u00f3w, poniewa\u017c wyniki mog\u0105 okaza\u0107 si\u0119 ca\u0142kiem zaskakuj\u0105ce.<\/p>\n<p>Na koniec jeszcze taka ma\u0142\u0105 dygresja odno\u015bnie algorytmu ewolucyjnego - operator mutacji powinien testowa\u0107 prawdodpobie\u0144stwo dla ka\u017cdego genu osobnika (czyli koloru w\u0119z\u0142a), a nie tak jak w tej implementacji, dla ca\u0142ego osobnika :).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>W tej notce opisz\u0119 moj\u0105 pierwsz\u0105 porz\u0105dn\u0105 styczno\u015b\u0107 z programem CodeAnalyst i przy okazji poka\u017c\u0119, w jaki spos\u00f3b uda\u0142o mi si\u0119 nieco zoptymalizowa\u0107 napisan\u0105 przeze mnie aplikacj\u0119 implementuj\u0105c\u0105 rozwi\u0105zywanie problemu kolorowania grafu za pomoc\u0105 algorytmu genetycznego. CodeAnalyst jest programem s\u0142u\u017c\u0105cym do profilowania aplikacji. Jego zadaniem jest zbieranie r\u00f3\u017cnego rodzaju pr\u00f3bek w trakcie dzia\u0142ania testowanej aplikacji, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[155,156,179,159,68,158,157],"_links":{"self":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/879"}],"collection":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/comments?post=879"}],"version-history":[{"count":126,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/879\/revisions"}],"predecessor-version":[{"id":1015,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/879\/revisions\/1015"}],"wp:attachment":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/media?parent=879"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/categories?post=879"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/tags?post=879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}