C++ – Nastoletni Programiści Thu, 15 Nov 2018 14:35:06 +0000 pl-PL hourly 1 https://wordpress.org/?v=4.9.8 115968029 Qt – Deployment aplikacji na Windowsa [dynamicznie] /qt-deployment-aplikacji-na-windowsa-dynamicznie/ /qt-deployment-aplikacji-na-windowsa-dynamicznie/#respond Wed, 22 Nov 2017 09:00:26 +0000 /?p=788 Czego unikać?

Bardzo popularnym, często powielanym błędem podczas deploymentu na Windowsa jest czekanie na błędy i wrzucanie bibliotek „na pałę” do folderu z binarką aplikacji, jest to pracochłonne, i działa tylko w niektórych przypadkach:

  • Aplikacja konsolowa Qt
  • Prosta aplikacja Qt Widgets (nie mylić z Qt Quick)

Trochę lepszym sposobem jest używanie dependency walker który, skanuje plik exe w poszukiwaniu zależności, i wyświetla brakujące biblioteki.

Prawidłowy i szybki deployment

Zacznę od aplikacji nieużywających komponentów Qt Quick , do tego celu użyjemy narzędzia windeployqt , znajduje się ono w katalogu Qt, podkatalogu wersji, podkatalogu mingw , i wreszcie w folderze bin w moim przypadku jest to C:/Qt/5.9/mingw53_32/bin .

Najczęściej popełnianym błędem, który i ja popełniłem jest użycie złej wersji narzędzia windeployqt , dlatego wcześniej warto się upewnić jakiej wersji Qt użyliśmy do budowy aplikacji.

Teraz czas uruchomić cmd i wywołać windeployqt wraz z odpowiednimi argumentami, w przypadku prostej aplikacji będzie to

Narzędzie automatycznie wykryje potrzebne biblioteki, znajdzie je i dorzuci do katalogu z naszym plikiem exe.

W przypadku aplikacji Qt Quick, należy dodatkowo załączyć adres projektu, wynika to z tego, że aplikacja wymaga dynamicznie zaczepionych plików qml od komponentów które zostały użyte, w tym celu używamy windeployqt zgodnie ze składnią:

Tu warto wspomnieć, że częstym błędem jest mylenie folderu projektu, z folderem qml który znajduje się obok folderu bin .

Przykładowe użycie:

Po więcej argumentów w razie potrzeby należy wywołać:

Polecam też zajrzeć na http://doc.qt.io/qt-5/windows-deployment.html .

]]>
/qt-deployment-aplikacji-na-windowsa-dynamicznie/feed/ 0 788
Sztuczna inteligencja w grach #01 – wstęp, zmysł wzroku oraz prosty debug-mode. /sztuczna-inteligencja-w-grach-01-wstep-zmysl-wzroku-oraz-prosty-debug-mode/ /sztuczna-inteligencja-w-grach-01-wstep-zmysl-wzroku-oraz-prosty-debug-mode/#comments Fri, 14 Apr 2017 12:27:34 +0000 /?p=1452 Sztuczna inteligencja w grach jest nieodłącznym elementem od dawien dawna. Jest ona mózgiem każdego wroga i NPC-ta. Kiedy programujemy A.I. mamy ogromne pole do popisu. Rozbudowywanie jej nie jest trudne i daje świetne efekty. Jest to element, który łatwo dostrzec i dzięki temu ludzie doceniają Twoją pracę jeszcze bardziej. W różnych grach można spotkać przeróżny poziom jakości zachowań postaci sterowanych komputerem. To jak dobre A.I. będą mieli wrogowie, często ma największy wpływ na końcową ocenę gry.

Niektóre z tytułów wręcz są popularne ze swojej prymitywnej S.I.:

Policjanci w GTA.

Źródło: http://www.memecenter.com/fun/509528/gta-sa-logic

Wbrew popularnej opinii sztuczna inteligencja policjantów w San Andreas nie jest aż taka prosta, jednak można by było ją trochę doszlifować. W tym artykule rozpoczniemy tworzenie podstawowego zmysłu dla przeciwników – zmysłu wzroku i wprowadzimy sobie prosty interfejs do debuggingu.

Zaczniemy od kodu finalnego z mojego poprzedniego artykułu: Jak programować gry? Najważniejsze elementy w grach 2D. Kod źródłowy z tamtego artykułu znajdziecie tutaj: GitHub

1. Przygotowanie sterowania i postaci pod grę.

Rozpoczniemy od stworzenia klasy gracza oraz klasy przeciwnika, które nazwiemy odpowiednio CPlayer oraz CEnemy . Następnie stworzymy prosty kontroler dla obydwu, a dodatkowo w kontrolerze gracza dodamy możliwość poruszania się.

Podobny schemat realizowałem już w filmie o tworzeniu bombermana, jednak przewiduję, że prawdopodobnie go nie obejrzałeś (bo komu chciałoby się oglądać w całości film o tworzeniu gry trwający 2 godziny). Z tego względu jeszcze raz wytłumaczę, co zrobiliśmy:

I) Utworzyliśmy klasę gracza i jego kontrolera:

  • CPlayer – klasa gracza
  • CPlayerController – klasa kontrolera gracza zawierająca sterowanie

Opisałem kod tak, by było dokładnie widać co krok po kroku trzeba wykonać.

Plik nagłówkowy Player.hpp :

Plik źródłowy Player.cpp :

II) Utworzyliśmy klasę wroga i jego kontrolera. Póki co nasze A.I. zostawimy w spokoju. Zajmiemy się nim w następnej części tego artykułu.

  • CEnemy – klasa wroga
  • CEnemyAIController – klasa kontrolera S.I. wroga

Plik nagłówkowy Enemy.hpp :

Plik źródłowy Enemy.cpp :

III) Wczytaliśmy potrzebne tekstury przy uruchamianiu gry:

IV) Stworzyliśmy gracza i jednego wroga, a następnie dodaliśmy ich do poziomu:

2. Utworzenie zmysłu wzroku.

Sztuczna inteligencja w grach jest oparta o różne zmysły np.: zmysł wzroku, zmysł słuchu czy nawet czasem zmysł węchu. Najprostszym do zaimplementowania jest zmysł wzroku, który jak zapewne łatwo jest się domyślić, będzie odpowiadał za widzenie. Będzie on działał w ten sposób, że będzie sprawdzał, czy jest coś interesującego w zasięgu wzroku oraz w obszarze widoku (kąt widzenia zrobimy łatwy do dostosowania) i jeśli coś wykryje, to zwróci to w wyniku.

Na początku, jako że wszystkie zmysły będą miały kilka wspólnych metod i własności, utworzymy sobie klasę bazową. Nazwałem ją IAISense (w swoich projektach przedrostek „I” dodaje do klas bazowych, dostarczających swego rodzaju interfejs do późniejszego rozbudowywania, jednak takich, które nie mogą być bezpośrednio używane). Do implementacji stworzymy sobie osobne pliki – AISense.hpp i AISense.cpp .

Teraz, zanim przejdziemy do kodu, zastanówmy się, co będzie musiał mieć każdy zmysł. Po chwili rozmyślań doszedłem do wniosku, że:

  • zmysł musi wiedzieć, kto jest jego właścicielem, gdyż każdy z nich musi znać np. pozycje właściciela;
  • zmysł musi posiadać jakąś listę postrzeganych aktorów;
  • zmysł musi dostarczać możliwość uaktualnienia postrzeganych aktorów (np. gdy ktoś wejdzie do pola widzenia albo wyjdzie z niego).

Będąc świadomym tego, do czego dążymy, utworzyłem następującą klasę bazową:

Warto bardzo dokładnie przeanalizować komentarze, szczególnie ten przy metodzie QueryActors, bo jest to kluczowa metoda tej klasy. Teraz do zaimplementowania został nam tak naprawdę tylko konstruktor (zwróć uwagę, że QueryActors jest metodą czysto wirtualną, GetSensedActors jest zdefiniowana jako inline ).

Kod konstruktora jest bardzo prosty:

Pokusiłem się tutaj o referencje na klasę IPawn (lub bazową) właściciela. Takie rozwiązanie wymusza podanie go (gdybyśmy użyli wskaźników, ktoś mógłby podać np. pusty wskaźnik), przez co reszta kodu, która się do niego odnosi, zadziała poprawnie.

Następnym etapem będzie stworzenie zmysłu wzroku. Aby mieć jakiś wgląd w sposób jego działania, spójrz na poniższy obraz:

Zatem mamy do stworzenia kolejną klasę, która będzie zawierała kąt i zasięg widzenia oraz implementacje metody QueryActors , która sprawdzi, czy aktorzy są polu widzenia. Zdecydowałem, że umieszczę tę klasę również w plikach AISense.hpp i AISense.cpp by nasz projekt nie miał za chwile 40 plików.

Tak oto wygląda ta klasa:

Tym razem do zaimplementowania mamy konstruktor oraz trzy metody. Może zajmijmy się najpierw konstruktorem:

Nie wrzuciłem ustawienia m_sightDistance i m_sightAngle do listy inicjalizacyjnej, ze względu na to, że mamy od tego odpowiednie funkcje, które zapobiegają wprowadzeniu nieodpowiednich danych.

Teraz spójrzmy dalej do metod SetSightDistance i SetSightAngle . Odpowiednie środki bezpieczeństwa są tutaj wymagane. Nie chcemy przecież mieć ujemnego zasięgu lub kąta widzenia > 180 stopni. Warto również zauważyć, że podane funkcje zwracają true , jeśli poprawnie ustawiono wartość a false , jeśli wartość była niepoprawna. Nie będziemy póki co z tego korzystać, ale warto mieć coś takiego zaimplementowanego – może niedługo się przyda 🙂

Przyszedł czas na kluczowy moment. Teraz zajmiemy się całą logiką zmysłu. Pomyślmy, co powinien on krok po kroku zrobić:

  • pobrać listę wszystkich aktorów ze sceny;
  • każdy aktor powinien być sprawdzony pod niżej wypisanymi kryteriami. Jeśli wszystkie z nich zostaną spełnione, dodajemy go do wynikowej listy aktorów;
    • aktor nie jest właścicielem tego zmysłu;
    • aktor znajduje się w odległości mniejszej niż zasięg wzroku;
    • kąt, pod którym znajduje się aktor względem właściciela, jest mniejszy niż kąt widzenia;
    • funkcja filtrująca pozwoliła aktorowi na dodanie do wyników.
  • zwrócić końcową listę aktorów.

Problemem jednak jest to, że w poprzednim artykule nie stworzyliśmy sobie metody, dzięki której zyskamy dostęp do listy aktorów ze sceny. Dlatego właśnie potrzebna jest nam poniższa funkcja, którą dodałem do klasy CLevel :

Na tych założeniach zbudowałem taką funkcję. Polecam przeanalizować po kolei każdy etap jej działania, gdyż właśnie jesteśmy w punkcie kulminacyjnym tego artykułu.

Uff… już najtrudniejsze za nami. Teraz możemy dodać już zmysł wzroku do wroga.
CSightSense m_sightSense; // Zmysl wzroku wroga
Należy również pamiętać o prawidłowym wywołaniu konstruktora zmysłu z poziomu listy inicjalizacyjnej. Następnie, aby nasz zmysł „działał” musimy go ciągle uaktualniać. Do tego posłuży nam metoda IActor::Update , którą sobie przeładujemy. Musimy jednak pamiętać, że metoda ta również ma swoją implementację w klasie bazowej IPawn , więc musimy umieścić też odwołanie do implementacji bazowej:

Co nam jednak z tego, że tak się narobiliśmy, a wciąż nie widzimy efektów? Właśnie dlatego teraz zaimplementujemy…

3. Widok informacji dla debuggingu

Wyszukiwanie błędów w grach jest bardzo uciążliwe, jeśli przed sobą mamy same cyferki i nic konkretnego. Dlatego właśnie wiele gier dodaje sobie prosty panel debugowania „in-game”. Tym właśnie się teraz zajmiemy.

Debugowanie w Gothic 2 NK

Na samym początku już wiemy, że potrzebna będzie nam funkcja generująca kształt wycinka koła. Jako że SFML sam jej nie dostarcza, postanowiłem napisać ją sam. Stworzyłem więc plik z deklaracją SFMLShapes.hpp oraz plik źródłowy SFMLShapes.cpp . Możliwe, że w przyszłości potrzebne będzie nam więcej własnych kształtów i wtedy umieścimy ich generowanie również w tych plikach.

Nazwałem ten kształt Pie (odnosi się do angielskiej nazwy ciasta, ponieważ nasz kształt to jakby wycinek ciasta 😉 ).

Powyższa funkcja nie jest w pełni funkcjonalna, jednak wystarcza do podstawowych zastosowań. Jej mankamentem jest to, że używa sf::ConvexShape (convex – wypukły). Może to spowodować niechciany efekt przy wyświetlaniu wycinka o kącie > 90 stopni. Jeśli ktoś ma na tyle ochoty, żeby się z tym bawić, to polecam do tego użyć sf::VertexArray .

Teraz już możemy wykorzystać ten generator. Pamiętamy jeszcze klasę IAISense ? Każdy zmysł będzie mógł się popisać jakimś fajnym symbolem przy debuggingu, dlatego też utworzymy metodę dla tej klasy, która będzie rysowała takie symbole na ekranie.

Metoda ta, nie jest metodą czysto wirtualną, bo zmysł ma mieć możliwość wyświetlenia debug info, ale nie jest do tego zmuszany.

Teraz czym do diabła jest to static bool DebugMode; ?

Dobrym pomysłem jest posiadanie jakiegoś przełącznika, którym będziemy sterowali, by albo włączyć tryb testowy, albo go wyłączyć. Statyczna zmienna DebugMode jest właśnie takim przełącznikiem. Oczywiście pamiętamy o tym, że taką zmienną statyczną trzeba też zainicjalizować. Najprościej będzie zrobić to na początku pliku źródłowego:

bool IAISense::DebugMode = true;

Mamy już bazę, to teraz trzeba zaimplementować wyświetlanie informacji trybu testowego dla zmysłu wzroku. Zrobiłem to tak:

Weźmy głęboki oddech… w tym miejscu artykuł ten możemy zakończyć jedną linijką, którą dodamy, by nasz debug info mógł się w ogóle wyświetlić. Jak zapewne się już domyśliłeś, umieścimy ją w metodzie wyświetlającej wroga na ekranie.

m_sightSense.DrawDebug();

4. Podsumowanie

W tym artykule zbudowaliśmy sobie podstawę pod dalsze rozwijanie modułu sztucznej inteligencji. Dodaliśmy również możliwość wyświetlania podstawowych informacji debuggingu. Kod z tego artykułu znajdziesz tutaj: GitHub . Teksturki można pobrać tutaj: Mega .

]]>
/sztuczna-inteligencja-w-grach-01-wstep-zmysl-wzroku-oraz-prosty-debug-mode/feed/ 2 1452
Kontenery w C++ – std::array, czyli ładniejsza tablica. /kontenery-w-c-stdarray-czyli-ladniejsza-tablica/ /kontenery-w-c-stdarray-czyli-ladniejsza-tablica/#comments Sun, 19 Feb 2017 13:33:02 +0000 /?p=1280 W standardowych i popularnych kursach języka C++ zazwyczaj nikt nie rusza tematów poświęconych szczegółom takim jak kontenery, czy też inne pierdoły z referencji języka – mogą one się wydać rozpraszające lub po prostu trudne dla początkującego, lub komuś po prostu się nie chce. Dlatego postanowiłem napisać serię poradników poświęconych właśnie takim szczegółom, które potrafią bardzo ułatwić życie a o których rzadko kiedy się pisze. Zakładam oczywiście, że czytelnik posiada podstawową wiedzę na temat C++’a, bo bez tego może być mu ciężko ugryźć ten poradnik.

Zacznę od podstawowego kontenera, jakim jest std::array .

Zakładam, że wiesz co to tablica, prawda? Ot, zbiór elementów jednego typu poukładanych w pamięci jeden za drugim. Nic strasznego, nic trudnego. Jest to bardzo prosta rzecz, można by rzec nawet, że aż za prosta – standardowy C-style array nie posiada w sobie nic ciekawego, żadnych iteratorów, żadnych fajnych metod dostępu do niego, wszystko trzeba robić „ręcznie”.

I tutaj na scenie pojawia się std::array z biblioteki array , dodany w standardzie C++11

Ważne – (zazwyczaj) domyślnie każdy kompilator korzysta ze standardu C++03. Żeby skorzystać z nowszych, należy to sprecyzować przy użyciu flagi -std=c++11/14/17 (zależnie od standardu), albo w opcjach kompilatora – ale to zostawiam już do samodzielnego ogarnięcia).

Jest to bardzo przyjemna alternatywa, posiadająca dużą wartość użytkową – po prostu ułatwia programiście życie. No dobra, ale co w tym takiego fajnego? Dlaczego miałbym się przestawić nagle na std::array , zamiast używać starego dobrego c-style arraya?

But… why?

Używanie std::array jest prawie takie samo jak używanie zwykłego c-style arraya – losowy dostęp do elementów umożliwia standardowy operator [] , ale oprócz tego ten kontener posiada kilka bajerów:

Po pierwsze – std::array posiada tak zwany iterator . Iterator to obiekt wskazujący na jakiś element z zakresu elementów, który potrafi iterować (czyli, potocznie mówiąc, „przeskakiwać” ) między elementami przy pomocy na przykład operatorów inkrementacji czy dekrementacji (ale częściej iteratorów używa się ich w range-based for’ach na przykład, o czym za chwilę). Jest to duże ułatwienie w sytuacjach gdzie musimy na przykład przelecieć całą tablicę od początku do końca (lub też od końca do początku, bo std::array posiada także reverse iterator!)

Po drugie – posiada przeładowane operatory porównania == , != , < , <= , > , >= . Czyli możemy w łatwy sposób porównywać dwa std::array’e. Dodatkowo, ma przeładowania dla std::swap i std::get , gdybyśmy potrzebowali w fikuśny sposób dostać się do danego elementu tablicy lub zamienić jej zawartość z zawartością innej.

Po trzecie – posiada kilka użytkowych metod, takich jak fill (wypełnienie tablicy pewną wartością), front / back (dostęp do pierwszego/ostatniego elementu), empty (sprawdzenie czy tablica jest pusta), size / max_size (sprawdzenie aktualnej i maksymalnej ilości elementów). Jest to też typ agregowalny, czyli przykładowo można go inicjalizować poprzez klamry {} , tak jak zwykłą tablicę.

Podsumowując – std::array jest prawie taki sam w użytkowaniu jak zwykły c-style array, tylko fajniejszy oraz często wygodniejszy.

No fajnie, tylko jak tego użyć?

Jak już wcześniej mówiłem, można używać std::array tak samo jak zwykłej tablicy. Inaczej wygląda deklaracja, ponieważ jest to zwyczajna struktura z szablonowymi typami w konstruktorze:

template<class T, std::size_t N> struct array;

Gdzie T to typ danych przechowywanych w naszym kontenerze, a N to jego początkowa wielkość. std::size_t to ogólnie mówiąc typ danych dla liczb całkowitych bez znaku.

Warto o tym pamiętać, polecam przy odnoszeniu się do wielkości kontenerów używać std::size_t zamiast int’a czy jakiegokolwiek innego typu danych, oszczędzi nam to ostrzeżeń kompilatora mówiących o porównywaniu typów ze znakiem i bez niego.

Przykładowe deklaracje takich kontenerów wyglądają następująco

No i fajnie, mamy jakieśtam tablice. Co teraz z nimi zrobić? Wykorzystajmy dla przykładu iteratory.

Ten kod chyba wymaga tłumaczenia, szczególnie jeśli ktoś wcześniej nie miał kontaktu z nowszymi standardami C++.

Więc tak – pierwszy przykład wyświetla nam zawartość std::array przy użyciu range-based for’a. const auto &v to stała referencja do kolejnych wartości kontenera, iterator automatycznie inkrementuje się co iterację pętli. Dlaczego odnoszę się do elementów w ten sposób? Mógłbym to zrobić klasycznie, poprzez int v , ale w ten sposób każdy z elementów byłby kopiowany do tymczasowej zmiennej v , co w przypadku większych kontenerów stwarzało by duży problem wydajnościowy. Odniesienie się poprzez referencję pozwala na kopiowanie jedynie adresu do elementu, unikając kopiowania jego samego, a const uniemożliwia zmianę danego elementu (bo nie chcemy go przecież zmieniać). W tym wypadku jest to na tyle mała skala że w praktyce nie zauważymy żadnych wzrostów wydajności, ale warto wyrobić sobie praktykę używania referencji. Jeśli chodzi o auto , to jest to tylko kwestia wygody – ten keyword i tak zostanie zamieniony przez kompilator na odpowiedni typ danych.

W następnym kawałku kodu wypełniamy sobie tablicę bezpośrednio odnosząc się do iteratorów – b.begin() wskazuje na początek, b.end() na koniec naszego kontenera (analogicznie, b.rbegin() wskazuje na koniec, a b.rend() na początek). Jak wcześniej mówiłem, iteratory mają przeładowane operatory – inkrementacja, dekrementacja i tego typu rzeczy, które są bardzo wygodne.

Dalej, modyfikacja elementów przy użyciu range-based for’a, tutaj używam referencji bez const ponieważ modyfikuję elementy (i w tym wypadku nie mogę odnieść się bez referencji, ponieważ bez niej operował bym na kopiach elementów tablicy)

I na koniec smaczek, przelatuję sobie tablicę od końca co drugi element.

Ale jak by to wyglądało na normalnej tablicy?

Oczywiście to tylko kilka przykładowych porównań, ale widzimy, że std::array jest o wiele bardziej czytelniejszy niż standardowa tablica.

Podsumowanie – plusy i minusy

Przyszedł czas na krótkie podsumowanie naszego kontenera

  • + Czytelny – posiada wiele funkcji które pozwalają na pisanie czytelnego kodu z wykorzystaniem naszego arraya
  • + Wygodny – Iteratory, przeładowania operatorów, metody pozwalające na sprawdzenie wielkości i dostęp do krańcowych elementów oraz wiele więcej metod i bajerów ułatwiających życie
  • + Kompatybilny – Można go używać w ten sam sposób, jak normalnej tablicy. Ale nie trzeba.
  • – Stała wielkość – Jeśli potrzebujemy kontenera o zmiennej wielkości, należy użyć na przykład std::vector

I w sumie tyle. Jeśli masz jakieś wątpliwości, nadal czegoś nie rozumiesz, zapomniałem o czymś, masz jakąś sugestię czy cokolwiek – pisz śmiało w komentarzu.

A po pełną referencję i więcej przykładów dla tego kontenera zapraszam tutaj: http://en.cppreference.com/w/cpp/container/array .

Następny rozdział będzie poświęcony kontenerowi std::vector.

]]>
/kontenery-w-c-stdarray-czyli-ladniejsza-tablica/feed/ 5 1280
Boost.SIMD, co tak naprawdę potrafi jeden rdzeń? /boost-simd-co-tak-naprawde-potrafi-jeden-rdzen/ /boost-simd-co-tak-naprawde-potrafi-jeden-rdzen/#comments Sat, 21 Jan 2017 18:44:27 +0000 http://nastoletni.pl/?p=925

Wolny program? Hurr durr, zrób wątki.

Brzmi znajomo? Jeśli program działa wolniej niż się chce inni programiści dają z reguły dwie rady: pierwsza, bardzo dobra – poprawić algorytm, druga, zwykle fatalna – użyć wielu wątków / procesów. Prawda jest taka, że czego by nam nie pchali producenci 12–rdzeniowych procesorów w telefonach wiele wątków ma tyle zalet co wad. Sama synchronizacja pochłania tyle mocy obliczeniowej, że o jednym rdzeniu można zapomnieć (drobna przesada nikogo nie zabiła :P), pełno miejsca na świeżutkie wycieki pamięci czy niezdefiniowane wyścigi po dane, jednym słowem – chaos.

Więc jak żyć?

Użyć jednego rdzenia, ale lepiej. Jak? Tu właśnie do akcji wkracza SIMD ( S ingle I nstruction, M ultiple D ata – z ang. jedna instrukcja, wiele danych). Sztuczka polega na klasycznym przeprowadzeniu jednej operacji jak dodawanie, odejmowanie czy dzielenie, ale zamiast jednego lub kilku operandów używa się całych zestawów danych, dobrze ilustruje to rysunek poniżej. Takie podejście do problemu znane jest od dawna, ale ręcznie wykorzystywane w praktyce dosyć rzadko, na rynku jest tak dużo procesorów, że programista podchodzący poważnie do tego co robi często nie wie czy jego program zostanie w ogóle uruchomiony na x86(_64), jak tu liczyć na konkretne rozszerzenie jak np. SSE4.2 , AVX2 albo NEON ? Dlatego w przypadku C++ (o nim głównie będzie ten wpis) liczyło się z reguły na kompilator, że zoptymalizuje to tu, to tam. Ale kompilator nie jest wszechwiedzący, dlatego dodatkowe 16 256–bitowych rejestrów w naszym nowiutkim procesorze może zostać niewykorzystane. A co jak co, 256–bitowe rejestry to nie lada zabawka.

Teraz przyszły jednak piękne czasy, kiedy na pytanie „SIMD?” odpowiadamy „ Boost.SIMD ”, tj. nazwą dość świeżej biblioteki z rodziny boost (swoją drogą polecam jeśli ktoś nie kojarzy), która za nas zajmie się doborem technologii, tyle że w przeciwieństwie do optymalizacji kompilatora możemy dostosować kod do takiego równoległego wykonania. Przykładowo iloczyn skalarny na procesorze wspierającym AVX (nie AVX2, to jest spotykane na zbyt niewielu procesorach żeby o tym pisać) można obliczyć ponad trzykrotnie szybciej – jednym rdzeniem.

Trochę praktyki

Już wiemy co i jak z tym Boost.SIMD i SIMD w ogóle, więc teraz trzeba sprawdzić jak to działa.

Uwaga – Boost.SIMD wymaga do pracy biblioteki boost w wersji 1.60 lub nowszej, inaczej pojawi się błąd związany z boost::enable_if (a może jeszcze jakiś inny).

Biblioteka jest na tyle dobrze zaprojektowana, że do nauczenia się jej obsługi w stopniu pozwalającym na przyśpieszenie wielu algorytmów wystarczy omówić prosty (ale nie za prosty) przykład, wybrałem wcześniej wspomniany produkt skalarny – idealnie pasuje do sytuacji (wydaje mi się, że ten przykład wykorzystano też w jakimś anglojęzycznym kursie, ale nie mogłem go znaleźć).
Na początek warto rozwiązać ten problem bez klawiatury, produkt skalarny można opisać tak: P = a 1 ×b 1 + a 2 ×b 2 + … + a n ×b n .

To teraz naiwna implementacja liczenia produktu dla dwóch instancji std::vector (z dowolnym alokatorem).

Poza komentarzami kod chyba nie wymaga dalszych wyjaśnień. Teraz pora na ciekawszą część z wykorzystaniem Boost.SIMD. Na początek kilka linii przygotowujących nas do właściwego wykorzystania biblioteki.

Warto pamiętać, że jeśli zamiast float wykorzystacie double ilość operacji prowadzonych na raz ( cardinal ) najprawdopodobniej zmaleje dwukrotnie. A wracając do produktu skalarnego, tutaj algorytm jest dalej prosty, ale już mniej oczywisty, najwygodniej opisać go zwykłym zdaniem:

Dzielimy vectory na pary paczek o rozmiarze cardinal , równolegle mnożymy paczki otrzymując nową o tym samym rozmiarze i doliczamy sumę jej elementów do produktu, jeżeli rozmiar vectorów nie jest wielokrotnością cardinal resztę doliczamy liniowo.

Teraz może wydawać się to troszkę zawiłe, ale kilka linii kodu rozwiewa wątpliwości:

Liczby proszę

Prosty teścik wydajności obu rozwiązań:

W moim przypadku (kompilowane z flagą -mavx ) liniowe rozwiązanie wykonywało się średnio 4522ms a to używające Boost.SIMD – 1305. Prosta matma mówi że to drugie jest ~3.47 raza szybsze, fajnie, nie? Ps. Nie tłumaczę kodu do testu, bo to głównie standard C++, jedyne o czym warto wspomnieć to alokator bs::allocator<float> – upewnia się że dane w wektorze są odpowiednio wyrównane co bywa niezbędne do sprawnego wykorzystania instrukcji SIMD, jednak nie wpływa w żaden (odczuwalny) sposób na czas wykonania liniowej implementacji.

Podsumowanie

I to by było na tyle. Wydaje się za proste? Mi też, ale ta wiedza wystarczy do wydajnego policzenia niejednego zadania, jeśli będziecie chcieli wiedzieć coś więcej to dokumentacja Boost.SIMD zaprasza. :). Bo nie łudźmy się, kod ode mnie jest szybki, ale nie najszybszy – trochę pracy i na procesorze z AVX powinniście móc osiągnąć co najmniej 6-krotny wzrost wydajności. Cały kod dla zainteresowanych .

]]>
/boost-simd-co-tak-naprawde-potrafi-jeden-rdzen/feed/ 3 925
Qt – Pierwszy projekt – Fake blue screen /qt-pierwszy-projekt-fake-blue-screen/ /qt-pierwszy-projekt-fake-blue-screen/#comments Sun, 25 Sep 2016 11:43:04 +0000 http://nastoletni.pl/?p=415 Okej, wstęp już za nami, wypadałoby zrobić jakiś mały programik. W tym poradniku pokażę, jak napisać program, który bardzo przypomina blue screena . Zastosowanie? Można wkręcać znajomych/nauczycieli, u mnie w szkole 4/4 informatyków się nabrało, w tym poradniku wykorzystamy część wiedzy z poprzedniego poradnika, wykorzystamy Qt Quick Controls 2, Qt 5.7.

Dobra, jak się za to zabrać? Zróbmy niebieski ekran, w tym celu do ApplicationWindow podpinamy właściwość, jaką jest kolor i ustawiamy na „blue”, teraz trzeba zrobić, aby nasza aplikacja nie wyglądała jak aplikacja, w tym celu dodajemy właściwość flags i wartość Qt.FramelessWindowHint , kod powinien wyglądać tak:

Teraz wypadałoby zmusić aplikację, aby była na pełnym ekranie. Jest parę sposobów, moim zdaniem najprościej będzie zaimportować moduł od okien import QtQuick.Window 2.0 , następnie ustalić width okna na Screen.width i analogicznie height .

Teraz potrzebny nam jakiś tekst, w tym celu do ApplicationWindow „podłączamy” komponent Text, ustalamy kolor, wielkość, font i inne ficzery.

Całość powinna wyglądać tak:

Nasz program właściwie jest już gotowy, można jeszcze dostosować font, wielkość itp. Co do tzw. deploymentu , czyli doprowadzania aplikacji do stanu, w którym można ją uruchomić na innym komputerze – opiszę ten proces w kolejnym poradniku, jest to głębszy temat.

]]>
/qt-pierwszy-projekt-fake-blue-screen/feed/ 1 415
Synchronizacja wątków w C# i C++ (Spinlock) /synchronizacja-watkow-w-c-i-c-spinlock/ /synchronizacja-watkow-w-c-i-c-spinlock/#comments Thu, 15 Sep 2016 13:18:45 +0000 http://nastoletni.pl/?p=309 Wielowątkowość, synchronizacja… Ale po co to komu?

Przytoczę pewne bardzo obrazowe porównanie. Wyobraź sobie, że jesteś w restauracji. Podnosisz menu, wybierasz potrawę i czekasz na kelnera. Widzisz, że tymczasowo obsługuje innego klienta. Mija kilka, kilkanaście minut, a kelner wciąż stoi przy stoliku innego klienta, obsługuje tylko jego. Dopiero, gdy klient płaci i wychodzi z restauracji, kelner podchodzi do Ciebie i zaczyna cię obsługiwać. W tym czasie przychodzi inny klient, ale kelner jest zajęty tylko tobą. Mniej więcej tak działa aplikacja jednowątkowa (nasz kelner). W oczywisty sposób jest to nieefektywne.

Wtedy niczym superbohater, przybywa wielowątkowość.

Super! A więc zyskujemy trochę wydajności. Jednak rozważmy ten kod w języku C# i przeanalizujmy go.

Co tu się dzieje? Program sprawdza czy plik examplefile.txt istnieje, jeżeli nie, tworzy go. Teraz przejdźmy do tego, dlaczego kod ten nie jest bezpieczny. Jako, że dziś mamy do czynienia z wieloma procesorami (rdzeniami), to w tle mogą działać równocześnie różne programy. Nie ma więc żadnej gwarancji, że jeden z nich nie utworzy pliku o nazwie examplefile.txt właśnie w tym katalogu. Rozchodzi się więc o moment pomiędzy otrzymaniem informacji, że plik nie istnieje, a utworzeniem go. Klasa tego błędu to tzw. Race Condition . Aby uniknąć takich sytuacji, wymagana jest synchronizacja.

Najprostszym używanym niskopoziomowym mechanizmem do synchronizacji są tzw. Spinlocki (w luźnym tłumaczeniu – kręcące się blokady). Jest on w zasadzie aktywną pętlą, która czeka, aż dany obiekt (blokada) zostanie zwolniony. Dziś w zasadzie się z nich nie korzysta, gdyż nowoczesne języki takie jak C# mają wbudowane mechanizmy synchronizujące, takie jak lock . Lock działa w ten sposób:

i jest równoważny temu:

gdyż lock jest w zasadzie aliasem tego drugiego. Teraz, zanim przejdę do spinlocków, wytłumaczę z pomocą Wikipedii czym jest owa sekcja krytyczna.

Sekcja krytyczna – fragment kodu programu, w którym korzysta się z zasobu dzielonego, a co za tym idzie w danej chwili może być wykorzystywany przez co najwyżej jeden wątek.

Myślę, że ta definicja spokojnie wystarczy. Przejdźmy teraz do Spinlocków.

W C++ można podejść do tego na prawdę prosto, jest to zwyczajnie (jak już pisałem) aktywna pętla. Wygląda to mniej więcej tak (korzystając z biblioteki <atomic> służącej właśnie do synchronizacji, i jak sama nazwa mówi do operacji atomowych):

Dzięki Spinlockom możemy także zsynchronizować uruchomienie wątków, aby wystartowały w mniej więcej tym samym czasie, co można zaimplementować bardzo prosto:

Wówczas nasza blokada może blokować(?) wykonanie wątku tak długo, jak programista sobie zażyczy. Słówka volatile użyłem, gdyż nie chce, by kompilator potraktował tę pętle tak:
if (slock != THREAD_COUNT) while(1); a tego oczywiście nie chcemy.

C# również udostępnia nam mechanizm spinlocków, ba, w zasadzie całą strukturę! Napisałem tu krótki program implementujący Spinlock:

Przeanalizujmy, co się w nim dzieje.


Zacznijmy tuż przed punktem wejścia do programu, tj. static void Main(string[] args) . Tworzymy w nim instancję struktury SpinLock, w bardzo standardowy sposób, oraz prywatne pole int o nazwie _count , którego zadaniem będzie przechowywanie pewnej wartości. Wewnątrz funkcji głównej tworzę prostą pętlę, w niej tworze natomiast nowy obiekt Task , który będzie asynchronicznie wykonywał naszą funkcję vInc() . Tam widzimy standardowy blok try finally ,  w którym to rozpoczynamy spin SpinLocka. Zauważ, że zanim wszedłem w aktywną pętlę, flagę lockTaken ustawiłem na false i jest przekazywana przez referencję, nie wartość.

To by było na tyle, jeżeli czegoś wystarczająco nie rozwinąłem, albo macie jakieś pytania, zachęcam do komentowania!


Źródła

  • https://msdn.microsoft.com/en-us/library/windows/desktop/ms683614(v=vs.85).aspx
  • http://en.cppreference.com/w/cpp/atomic/atomic_flag
  • http://www.c-sharpcorner.com/UploadFile/1d42da/spinlock-class-in-threading-C-Sharp/
  • https://camedcomputing.wordpress.com/2016/03/31/wielowatkowosc-w-c-threading-locki-spinlocki /

]]>
/synchronizacja-watkow-w-c-i-c-spinlock/feed/ 4 309
Qt – Wprowadzenie /qt-wprowadzenie/ /qt-wprowadzenie/#comments Wed, 31 Aug 2016 15:38:14 +0000 http://beta.nastoletni.pl/?p=51

Qt – zestaw przenośnych bibliotek i narzędzi programistycznych dedykowanych dla języków C++ , QML i Java . Ich podstawowym składnikiem są klasy służące do budowy graficznego interfejsu programów komputerowych , począwszy od wersji 4.0 Qt zawiera też narzędzia do tworzenia programów konsolowych i serwerów. – Wikipedia

Może zacznę od motywacji, pisząc w C++ największym problemem jest multipletowość, niby jest, ale w praktyce trzeba się natrudzić żeby przepisać średnio zaawansowaną aplikacje z np. windowsa na linuksa, z pomocą przychodzi Qt, gdzie mamy gotową obsługę sieci, baz danych, zapytań, obsługi wielu wątków, obsługę jsona, xmla i wielu innych bardzo przydatnych pierdół. Przekonany?

Ale halo? Qt jest płatne , na co ty mnie naciągasz? Owszem, przy próbie pobrania na pierwszy rzut widać tylko możliwość kupienia/subskrypcji na zasadach trial, ale niżej jest opcja pobrania wersji otwartej, Community która prawie się nie różni od wersji płatnej.

No dobra, pobrałem Qt, odpalam Qt Creator, co teraz?

Jeżeli znasz już podstawy C++ to super, jeżeli nie to odsyłam do książek lub Zelenta, ale i bez tego na start sobie poradzisz, no dobra, chcesz stworzyć projekt, ale tyle opcji do wybrania, co wybrać? I tu pojawia się kolejny problem z jakim początkujący walczą.
Najprostszym rozwiązaniem wydaje się Qt Widgets, ale czy aby na pewno? W Qt Widgets wszystko piszemy C++, ktoś może pomyśleć że super, znam C++ będzie łatwo, projektowanie interfejsu aplikacji używając samego C++ to jakieś nieporozumienie, cytując klasyka, co prawda jest opcja tworzenia interfejsu używając Designera, czyli specjalnego narzędzia dołączonego do Qt Creatora, jednak przy średnio zaawansowanych projektach designer często się crashuje i są duże problemy, lepiej się od niego nie uzależniać. Co więc wybrać na początek? Zdecydowanie polecam stworzenie projektu używając Qt Quick Controls, obecna najnowsza wersja to Qt Quick Controls 2, interfejs można malować używając banalnego języka frontendowego QML który składnią bardzo przypomina CSS.

Pierwsza aplikacja z Qt Quick

Ujrzymy chaos, nasz projekt składa się z:

  • projekt.pro – W nim są informacje o modułach używanych przez aplikacje, w przypadku np. działania przy sieci trzeba dodać moduł network, ale to w innym poradniku/w dokumentacji
  • deployment.pri – Tam są wskazówki dla kompilatora, najlepiej tego nie dotykać na początku
  • main.cpp – Backend aplikacji, w tym miejscu będziemy importować klasy cpp itp.
  • main.qml – Główny plik z designem
  • mainform – Układ designu, polecam usunąć, na początku wprowadza tylko niepotrzebny chaos i niezrozumienie.

Zróbmy czerwony kwadrat na środku ekranu a w nim napis Witam .
W tym celu skracamy nasz kod do:

  • visible – proporcja która definiuje to czy dany komponent jest widoczny
  • width / height – szerokość / wysokość komponentu
  • title – tytuł

Teraz dodajmy prostokąt, jak nazywa się prostokąt po angielsku? – rectangle , nazwy komponentów zawsze piszemy z dużej litery, a zmiennych z małej.

Więc zróbmy prostokąt

Dobra, właściwości width i height chyba nie muszę ponownie tłumaczyć. color, definiuje kolor, można go zapisać na wiele sposobów, czytaj więcej .

Pojawiła się nowa zmienna – anchors.centerIn , zmienna ta definiuje to, że obiekt ma być w środku jakiegoś innego obiektu, parent to id obiektu na którego środku ma się znajdować nasz prostokąt, parent oznacza obiekt nadrzędny, ten który w hierarchii jest wyżej, warto zapamiętać. Równie dobrze w tym miejscu można wpisać jakieś id, każdy komponent może przyjmować wartość id, zaprezentuje to w kolejnym przykładzie niżej.

No okej, mamy okno, mamy jakiś brzydki, czerwony prostokąt, czas na tekst, jak to zrobić?
Analogicznie, do komponentu Rectangle należy „podczepić” komponent Text , robimy to w taki sam sposób jak Rectangle do ApplicationWindow’a , wypadałoby go wycentrować, użyj do tego anchors.centerIn , tekst ustalamy zmienna text, przykład text: Witam
To twoje zadanie domowe Możesz też poeksperymentować i zamiast tekstu dać Button , w tym celu zajrzyj do dokumentacji Qt Quick.

]]>
/qt-wprowadzenie/feed/ 6 51