Realizacja i testowanie algorytmu ewolucji różnicowej z użyciem programowalnych kart graficznych.

Podobne dokumenty
ROZWÓJ ALGORYTMU EWOLUCJI RÓŻNICOWEJ. Konrad Wypchło

Algorytm genetyczny (genetic algorithm)-

Metody Rozmyte i Algorytmy Ewolucyjne

Aproksymacja funkcji a regresja symboliczna

Strategie ewolucyjne (ang. evolu4on strategies)

Dobór parametrów algorytmu ewolucyjnego

Strategie ewolucyjne. Gnypowicz Damian Staniszczak Łukasz Woźniak Marek

CUDA Median Filter filtr medianowy wykorzystujący bibliotekę CUDA sprawozdanie z projektu

Programowanie współbieżne Wykład 2. Iwona Kochańska

Zastosowanie technologii nvidia CUDA do zrównoleglenia algorytmu genetycznego dla problemu komiwojażera

w analizie wyników badań eksperymentalnych, w problemach modelowania zjawisk fizycznych, w analizie obserwacji statystycznych.

Algorytmy ewolucyjne. Łukasz Przybyłek Studenckie Koło Naukowe BRAINS

Spis treści. I. Skuteczne. Od autora... Obliczenia inżynierskie i naukowe... Ostrzeżenia...XVII

Algorytm Genetyczny. zastosowanie do procesów rozmieszczenia stacji raportujących w sieciach komórkowych

ALGORYTMY GENETYCZNE ćwiczenia

Algorytmy genetyczne w optymalizacji

Porównanie wydajności CUDA i OpenCL na przykładzie równoległego algorytmu wyznaczania wartości funkcji celu dla problemu gniazdowego

Podstawy Informatyki Systemy sterowane przepływem argumentów

Zadanie 5 - Algorytmy genetyczne (optymalizacja)

Programowanie procesorów graficznych GPGPU

Teraz bajty. Informatyka dla szkół ponadpodstawowych. Zakres rozszerzony. Część 1.

SCHEMAT ROZWIĄZANIA ZADANIA OPTYMALIZACJI PRZY POMOCY ALGORYTMU GENETYCZNEGO

Skalowalność obliczeń równoległych. Krzysztof Banaś Obliczenia Wysokiej Wydajności 1

Programowanie równoległe i rozproszone. Praca zbiorowa pod redakcją Andrzeja Karbowskiego i Ewy Niewiadomskiej-Szynkiewicz

PLAN WYKŁADU OPTYMALIZACJA GLOBALNA OPERATOR KRZYŻOWANIA ETAPY KRZYŻOWANIA

i3: internet - infrastruktury - innowacje

Zadania badawcze prowadzone przez Zakład Technik Programowania:

INŻYNIERIA OPROGRAMOWANIA

Programowanie obiektowe - 1.

ALHE Z11 Jarosław Arabas wykład 11

Generowanie i optymalizacja harmonogramu za pomoca

CMAES. Zapis algorytmu. Generacja populacji oraz selekcja Populacja q i (t) w kroku t generowana jest w następujący sposób:

WAE Jarosław Arabas Pełny schemat algorytmu ewolucyjnego

WAE Jarosław Arabas Adaptacja i samoczynna adaptacja parametrów AE Algorytm CMA-ES

Przygotowanie kilku wersji kodu zgodnie z wymogami wersji zadania,

Algorytmy ewolucyjne 1

Strategie ewolucyjne (ang. evolution strategies)

Algorytmy genetyczne

Programowanie Strukturalne i Obiektowe Słownik podstawowych pojęć 1 z 5 Opracował Jan T. Biernat

Praca dyplomowa magisterska

Tesla. Architektura Fermi

ALHE Jarosław Arabas Metaheurystyki w Rn Ewolucja różnicowa EDA CMAES Rój cząstek

Struktura systemu operacyjnego. Opracował: mgr Marek Kwiatkowski

Tworzenie programów równoległych cd. Krzysztof Banaś Obliczenia równoległe 1

INFORMATYKA, TECHNOLOGIA INFORMACYJNA ORAZ INFORMATYKA W LOGISTYCE

Metody eksploracji danych w odkrywaniu wiedzy (MED) projekt, dokumentacja końcowa

ZMODYFIKOWANY Szczegółowy opis przedmiotu zamówienia

Ewolucja Różnicowa Differential Evolution

1 Wprowadzenie do algorytmiki

Podstawy Programowania C++

Algorytmy genetyczne

Optymalizacja optymalizacji

Optymalizacja ciągła

Algorytm grupowania danych typu kwantyzacji wektorów

Algorytmy genetyczne

Obliczenia równoległe i rozproszone. Praca zbiorowa pod redakcją Andrzeja Karbowskiego i Ewy Niewiadomskiej-Szynkiewicz

Algorytmy ewolucyjne NAZEWNICTWO

Architektura komputerów

Programowanie procesorów graficznych NVIDIA (rdzenie CUDA) Wykład nr 1

Lekcja 3: Pierwsze kroki z Pythonem. Pętle

Algorytmy ewolucyjne - algorytmy genetyczne. I. Karcz-Dulęba

SPOSOBY POMIARU KĄTÓW W PROGRAMIE AutoCAD

Algorytmy metaheurystyczne Wykład 6. Piotr Syga

OPTYMALIZACJA HARMONOGRAMOWANIA MONTAŻU SAMOCHODÓW Z ZASTOSOWANIEM PROGRAMOWANIA W LOGICE Z OGRANICZENIAMI

Zapoznanie z technikami i narzędziami programistycznymi służącymi do tworzenia programów współbieżnych i obsługi współbieżności przez system.

Zarządzanie pamięcią operacyjną

SCENARIUSZ LEKCJI. Streszczenie. Czas realizacji. Podstawa programowa

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Kryptografia na procesorach wielordzeniowych

Analiza i projektowanie oprogramowania. Analiza i projektowanie oprogramowania 1/32

Podstawy OpenCL część 2

//warunki początkowe m=500; T=30; c=0.4; t=linspace(0,t,m); y0=[-2.5;2.5];

Algorytmy genetyczne. Paweł Cieśla. 8 stycznia 2009

LABORATORIUM 4: Algorytmy ewolucyjne cz. 2 wpływ operatorów krzyżowania i mutacji na skuteczność poszukiwań AE

Algorytm. a programowanie -

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery

Algorytm hybrydowy dla alokacji portfela inwestycyjnego przy ograniczonych zasobach

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk

Podstawy programowania Laboratorium. Ćwiczenie 2 Programowanie strukturalne podstawowe rodzaje instrukcji

Algorytmy i Struktury Danych

Strefa pokrycia radiowego wokół stacji bazowych. Zasięg stacji bazowych Zazębianie się komórek

REFERAT PRACY DYPLOMOWEJ

Algorytm dyskretnego PSO z przeszukiwaniem lokalnym w problemie dynamicznej wersji TSP

Nowoczesne technologie przetwarzania informacji

6. Algorytmy ochrony przed zagłodzeniem dla systemów Linux i Windows NT.

Ataki na RSA. Andrzej Chmielowiec. Centrum Modelowania Matematycznego Sigma. Ataki na RSA p. 1

przetworzonego sygnału

Scenariusz lekcji opartej na programie Program nauczania informatyki w gimnazjum DKW /99

INFORMATYKA W SZKOLE. Podyplomowe Studia Pedagogiczne. Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227

Przetwarzanie Równoległe i Rozproszone

16. Taksonomia Flynn'a.

Kurs programowania. Wykład 12. Wojciech Macyna. 7 czerwca 2017

Programowanie genetyczne, gra SNAKE

Pisząc okienkowy program w Matlabie wykorzystujemy gotowe obiekty graficzne, lub możemy tworzyć własne obiekty dziedzicząc już zdefiniowane.

Optymalizacja. Przeszukiwanie lokalne

Modyfikacje i ulepszenia standardowego algorytmu genetycznego

lekcja 8a Gry komputerowe MasterMind

EXSO-CORE - specyfikacja

Transkrypt:

Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych Instytut Informatyki Rok akademicki 2012/2013 Praca dyplomowa inżynierska Michał Szczepankiewicz Realizacja i testowanie algorytmu ewolucji różnicowej z użyciem programowalnych kart graficznych. Opiekun pracy: prof. nzw. dr hab. inż. Jarosław Arabas Ocena...... Podpis Przewodniczacego Komisji Egzaminu Dyplomowego

Specjalność: Informatyka Inżynieria Systemów Informatycznych Data urodzenia: 11 kwietnia 1989 r. Data rozpoczęcia studiów: 1 października 2008 r. Życiorys Urodziłem się 11 kwietnia 1989 roku w Kolnie. W latach 2002-2005 uczęszczałem do Gimnazjum w Kolnie. W roku 2008 ukończyłem Liceum Ogólnokształcace nr 1. im. Tadeusza Kościuszki w Łomży, gdzie uczęszczałem do klasy o profilu matematyczno-fizycznym. Edukację na szczeblu średnim zakończyłem zdaniem w maju 2008 r. egzaminu dojrzałości. Od 2008 jestem studentem kierunku Informatyka na Wydziale Elektroniki i Technik Informacyjnych Politechniki Warszawskiej.... podpis studenta Egzamin dyplomowy Złożył egzamin dyplomowy w dn.... z wynikiem... Ogólny wynik studiów... Dodatkowe wnioski i uwagi Komisji......

Streszczenie Tematem pracy jest realizacja i testowanie algorytmu ewolucji różnicowej z użyciem programowalnych kart graficznych. W pracy zawarty jest opis metod ewolucji różnicowej oraz wykorzystanej technologii CUDA. Prace projektowe doprowadziły do zmodyfikowania platformy EASEA, co umożliwiło generowanie implementacji algorytmu ewolucji różnicowej z użyciem tego środowiska. Omówiona jest także architektura dodatkowo utworzonej aplikacji w języku C++ oraz CUDA C, której implementacja nastawiona jest na uzyskanie jak największego przyspieszenia przy pomocy programowalnych kart graficznych. W pracy zawarte jest omówienie wad i zalet obu stworzonych aplikacji, możliwe dalsze drogi ich rozwoju oraz porównanie wydajności. Zamieszczone i omówione sa w niej również wyniki przeprowadzonych testów efektywności algorytmu ewolucji różnicowej. Słowa kluczowe: algorytmy ewolucyjne, ewolucja różnicowa, CUDA, EASEA Abstract Title: Implementation and testing of differential evolution algorithm with usage of general purpose graphics processing units. The main purpose of the thesis was to create and test an implementation of differential evolution algorithm with usage of general purpose graphics processing units. The design works led to modifications of EASEA platform that enabled generation of differential evolution algorithm implementation. At first, the ideas of differential evolution and NVIDIA CUDA technology are described. Discussed is the architecture of the application developed in C++ and CUDA C programming languages which was additionally created to show possible performance improvements. The thesis contains the comparison of both implementations and analyses possible areas for further modifications to be done in the future. The differential evolution algorithm was tested in solving global optimization problems and the results are discussed. Key words: evolutionary algorithms, differential evolution, CUDA, EASEA

Spis treści 1. Wstęp............................................ 1 1.1. Cel pracy inżynierskiej............................... 2 1.2. Układ pracy..................................... 2 1.3. Słownik pojęć i oznaczeń.............................. 4 2. Ewolucja różnicowa................................... 6 2.1. Algorytmy ewolucyjne................................ 6 2.1.1. Metody selekcji............................... 7 2.1.2. Operator krzyżowania............................ 8 2.1.3. Operator mutacji.............................. 9 2.1.4. Metody sukcesji............................... 9 2.2. Klasyczna wersja algorytmu ewolucji różnicowej................ 9 2.3. Warianty algorytmu ewolucji różnicowej..................... 10 2.4. Modyfikacje algorytmu............................... 12 2.5. Wybrane zagadnienia................................ 13 2.5.1. Środek geometryczny populacji...................... 13 2.5.2. Kostkowe ograniczenia dziedziny..................... 14 3. Programowalne karty graficzne............................ 17 3.1. NVIDIA CUDA.................................... 18 3.1.1. Model programowania........................... 19 3.1.2. Architektura wewnętrzna......................... 21 3.1.3. Wybrane funkcjonalności......................... 22 3.2. Implementacje DE na GPGPU........................... 23 4. Implementacja algorytmu DE w platformie EASEA................ 24 4.1. Struktura platformy EASEA............................ 25 4.1.1. Biblioteka LibEASEA............................ 25 4.1.2. Pliki konfiguracyjne............................. 26 4.1.3. Pliki szablonowe............................... 28 4.1.4. Generator kodu............................... 28 4.2. Modyfikacje platformy EASEA........................... 29 4.2.1. Rozszerzenia generatora kodu....................... 30 4.2.2. Kompilacja platformy EASEA z rozszerzeniami w generatorze kodu. 33 4.3. Sposób użycia algorytmu DE w środowisku EASEA.............. 34 5. Testowanie jakości wyników uzyskiwanych przez algorytm ewolucji różnicowej......................................... 38 5.1. Zestaw funkcji testowych CEC2005....................... 38 5.2. Modyfikacje benchmarku CEC2005....................... 39 5.3. Ustawienia algorytmu w przeprowadzonych testach.............. 41 5.4. Wyniki........................................ 42 6. Własna implementacja algorytmu DE z wykorzystaniem technologii CUDA. 47 6.1. Środowisko pracy i karta graficzna NVIDIA GTX 680.............. 47 6.2. Zrównoleglenie algorytmu............................. 48 6.3. Reprezentacja i przechowywanie danych..................... 48 6.4. Losowanie danych.................................. 49 6.5. Szczegóły implementacji.............................. 49 6.5.1. Wykorzystanie pamięci niepodlegajacej wymianie........... 49 6.5.2. Tworzenie osobników potomnych..................... 50

Spis treści ii 6.5.3. Obliczanie funkcji celu........................... 51 6.5.4. Redukcja populacji poprzez sukcesję elitarna.............. 52 6.5.5. Znalezienie najlepszej wartości funkcji celu............... 54 6.5.6. Obliczenie środka geometrycznego populacji.............. 56 6.5.7. Wybór wielkości bloku........................... 57 7. Porównanie implementacji w języku C++ oraz CUDA C z implementacja wykonana w środowisku EASEA............................ 58 7.1. Różnice w wykorzystaniu karty graficznej między EASEA a implementacja w C++ i CUDA C................................... 58 7.2. Wykonane testy wydajnościowe.......................... 59 7.3. Zalety i wady programów.............................. 64 8. Wnioski........................................... 66 Bibliografia.......................................... 69 A. Załaczniki do pracy inżynierskiej........................... 71 B. Definicje funkcji wykorzystanych w testowaniu efektywności algorytmu ewolucji różnicowej.................................... 72 B.1. Zastosowane oznaczenia.............................. 72 B.2. Funkcja F1: przesunięta funkcja sferyczna................... 72 B.3. Funkcja F2: przesunięta funkcja Schwefela................... 72 B.4. Funkcja F9: przesunięta funkcja Rastrigina................... 74 B.5. Funkcja F11: przesunięta i obrócona funkcja Weierstrassa.......... 74 B.6. Funkcja F12: zagadnienie Schwefela....................... 75 B.7. Funkcja F15: funkcja złożona........................... 75

1. Wstęp Idea algorytmów ewolucyjnych, których reguły działania inspirowane sa naturalnymi procesami biologicznymi, sięga lat siedemdziesiatych XX wieku. Ich rozwój w ośrodkach badawczych i naukowych doprowadził z czasem do powstania wielu różnorodnych koncepcji ich pracy. Algorytmy ewolucyjne sa szczególnie przydatne w sytuacji, gdy stworzenie dokładnego modelu matematycznego problemu jest trudne lub wręcz niemożliwe (np. ze względu na wymagania czasowe), gdyż pozwalaja na uzyskanie rozwiazań, które nawet jeżeli nie sa optymalne, moga zostać uznane za satysfakcjonujace. Algorytmy ewolucyjne stały się z czasem tak popularne, że powstały specjalne metodyki testowania pozwalajace nie tylko na dostrajanie pracy algorytmu poprzez regulację jego wewnętrznych parametrów, ale także na porównanie jakości działania różnych algorytmów ewolucyjnych. Stworzone zostały też niezależne platformy, które bazujac na wspólnych elementach rodzin algorytmów ewolucyjnych, pozwalaja na zautomatyzowane tworzenie ich implementacji. Przykładem takiego środowiska jest EASEA. Jedna z szybko rozwijajacych się gałęzi szeroko pojętych algorytmów ewolucyjnych jest metoda ewolucji różnicowej, która została zaprezentowana w połowie lat 90. ubiegłego wieku. Wykorzystanie algorytmów ewolucji różnicowej, pomimo zaskakujacej prostoty i łatwości w zrozumieniu reguł rzadz acych ich działaniem, pozwala na osiagnięcie rezultatów bardzo dobrych jakościowo. W zwiazku ze stale rosnacym stopniem skomplikowania współczesnych problemów optymalizacyjnych, istotny stał się nie tylko dobór odpowiednich metod ich rozwiazywania, ale także technologii zapewniajacych uzyskanie akceptowalnego czasu wykonania. Poczatkowo główna odpowiedzia producentów układów komputerowych na zwiększajace się wymagania na moc obliczeniowa były ciagłe prace nad przyspieszaniem taktowania układów. Szybko jednak doprowadziło to do fizycznej granicy zwiazanej z niewystarczajacymi sposobami odprowadzania ciepła od układów cyfrowych. Podjęto wtedy inne drogi zwiększania wydajności systemów komputerowych, które polegały na możliwości zrównoleglania wykonywanych na nich zadań. Najlepszym tego przykładem jest postać dzisiejszych programowalnych kart graficznych ogólnego przeznaczenia (GPGPU), których budowa wewnętrzna

1.1. Cel pracy inżynierskiej 2 opiera się na setkach, a nawet tysiacach rdzeni. Pełne wykorzystanie tych zasobów przez implementację algorytmu ewolucji różnicowej pozwala na znaczne skrócenie czasu potrzebnego na uzyskanie rozwiazań, co zostanie pokazane w niniejszej pracy. 1.1. Cel pracy inżynierskiej Celem niniejszej pracy była realizacja i testowanie algorytmu ewolucji różnicowej z wykorzystaniem programowalnych kart graficznych. Wymagało to zapoznania się z zagadnieniami ewolucji różnicowej, architektura środowiska EASEA oraz możliwościami dzisiejszych układów GPGPU. Efektem prac było stworzenie oraz przetestowanie w platformie EASEA szablonu, który umożliwia generowanie przez to środowisko sparametryzowanej implementacji algorytmu ewolucji różnicowej. Powstałe modyfikacje maja zapewniona pełna integralność z istniejac a platforma, tzn. umożliwiaja generowanie kodu wykonujacego się wyłacznie na jednostce centralnej lub wspomaganego wykorzystaniem karty graficznej. Wykonane rozwiazanie zostało przetestowane pod katem jakości otrzymywanych rozwiazań poprzez wykorzystanie w tym celu jednego z najpopularniejszych zestawów funkcji testowych. Podczas prac wykorzystano benchmark CEC2005. Powstałe rozwiazanie przetestowano pod katem wydajności czasowej. Dla możliwości pełnego przegladu tego zagadnienia, w trakcie realizacji prac dodatkowo wykonano własna implementację algorytmu ewolucji różnicowej wykorzystujac a możliwości dzisiejszych kart graficznych. 1.2. Układ pracy Drugi rozdział niniejszej pracy opisuje algorytm ewolucji różnicowej. Zawiera on specyfikację sposóbu jego działania, przykładowe odmiany wykorzystywane współcześnie, a także wybrane zagadnienia algorytmów ewolucyjnych, których wykorzystanie pozwoliło na zwiększenie jakości generowanych rozwiazań. W kolejnym rozdziale opisana jest wykorzystana w pracy technologia CUDA firmy NVIDIA, która jest najpopularniejsza współczesna realizacja programowalnych kart graficznych. Omówiony jest model programowania oraz architektura wewnętrzna kart graficznych tego producenta, a także te funkcjonalności, które wydawały się być najbardziej przydatne w kontekście własnej implementacji algorytmu ewolucji różnicowej. Czwarty rozdział zawiera opis środowiska EASEA, które umożliwia generowanie implementacji algorytmów ewolucyjnych. Omówiona jest architektura tego oprogramowania wraz z wyróżnionymi w niej niezależnymi blokami. Przedstawione sa także wszystkie wprowadzone modyfikacje środowiska EASEA, które

1.2. Układ pracy 3 umożliwiły rozszerzenie jego funkcjonalności o generowanie implementacji algorytmu ewolucji różnicowej. Kolejny rozdział prezentuje proces testowania algorytmu ewolucji różnicowej oraz analizy jakości uzyskiwanych wyników. Zawiera omówienie benchmarku CEC2005, jak również opis wprowadzonych w nim modyfikacji pozwalajacych na uruchomienie obliczania jego funkcji na karcie graficznej. Na koniec omówione sa uzyskane rezultaty. W rozdziale szóstym znajduje się opis wykonanej dodatkowo własnej implementacji algorytmu z wykorzystaniem technologii programowalnych kart graficznych. Zaprezentowano w nim środowisko pracy, a także szczegóły implementacyjne pozwalajace na zwiększenie wydajności obliczeń. Rozdział siódmy zawiera porównanie architektur oprogramowania generowanego przez środowisko EASEA z własna implementacja. Zaprezentowane i porównane sa tam wyniki wydajnościowe uzyskane przez obie aplikacje. Na koniec omówione sa wady i zalety obu programów, jak również ewentualne drogi ich dalszego rozwoju. Ostatni rozdział stanowi podsumowanie całości zadań wykonanych przy realizacji niniejszej pracy dyplomowej oraz osiagniętych rezultatów.

1.3. Słownik pojęć i oznaczeń 4 1.3. Słownik pojęć i oznaczeń osobnik - pojedynczy punkt przestrzeni poszukiwań, który jest przetwarzany przez algorytm ewolucyjny populacja - zbiór osobników w kolejnej generacji algorytmu ewolucyjnego eksploatacja - proces poszukiwania położenia optimum lokalnego w jego otoczeniu eksploracja - proces poszukiwania otoczenia optimum lokalnego t - indeks generacji P t - populacja w generacji o numerze t Pi t - punkt o indeksie i w populacji P t Pi,j t - współrzędna o numerze j punktu o indeksie i w populacji P t V t - populacja osobników potomnych w generacji t w algorytmie ewolucyjnym p c - prawdopodobieństwo zajścia krzyżowania w algorytmie ewolucyjnym µ - liczność populacji n - wymiarowość problemu Ω - przestrzeń poszukiwań D - zbiór dopuszczalny w przestrzeni poszukiwań l j - dolna granica j-tego wymiaru zbioru D u j - górna granica j-tego wymiaru zbioru D f : R n R - funkcja celu (ang. fitness function) minimalizowana przez algorytm ewolucyjny x opt - punkt minimalizujacy funkcję f w hipersześcianie D x best - punkt o najlepszej wartości funkcji celu znaleziony w wyniku działania algorytmu Pmid t - środek geometryczny populacji w generacji t F - współczynnik skalujacy (ang. scaling factor) w algorytmach z rodziny ewolucji różnicowej CR - stała krzyżowania (ang. crossover rate) w algorytmach z rodziny ewolucji różnicowej, CR [0, 1] urzadzenie nadrzędne (ang. host) - określenie używane w odniesieniu jednostki centralnej urzadzenie podrzędne (ang. device) - określenie używane w odniesieniu do karty graficznej benchmark - określenie używane w odniesieniu do zestawu funkcji testowych Pojęcia zwiazane z technologia NVIDIA CUDA: jadro (ang. kernel) - funkcja wykonywana przez każdy uruchomiony watek blok (ang. block) - grupa watków siatka (ang. grid) - grupa bloków

1.3. Słownik pojęć i oznaczeń 5 osnowa (ang. warp) - grupa 32 watków zarzadzana przez multiprocesor graficzny

2. Ewolucja różnicowa Metody ewolucji różnicowej sa stosunkowo nowym pojęciem w dziedzinie algorytmów ewolucyjnych. Algorytm ewolucji różnicowej został zaproponowany w 1995 roku przez Kennetha V. Price a i Rainera Storna przy próbie rozwiazania zadania aproksymacji wielomianami Czebyszewa. Już od momentu zaprezentowania, podejście to zostało poddawane licznym testom wydajnościowym oraz efektywnościowym i potwierdziło swoja skuteczność na konferencjach w Nagoi (Japonia, 1996) oraz Indianapolis (USA, 1997). Pomimo niewielkiej liczby parametrów oraz prostoty operatorów składajacych się na algorytm, ewolucja różnicowa dostarczała dobrych rezultatów przy rozwiazywaniu zadań optymalizacyjnych. Cechuje się także łatwościa użycia i wydajnościa, stad szybko została zaadaptowana w inżynierii, statystyce i finansach, a algorytm w wersji zaprezentowanej przez Price a i Storna zapoczatkował cała rodzinę algorytmów ewolucji różnicowej. 2.1. Algorytmy ewolucyjne Algorytmy ewolucyjne wykorzystywane sa głównie do rozwiazywania problemów optymalizacji globalnej. Zadanie optymalizacji globalnej funkcji zmiennych rzeczywistych zdefiniowane jest następujaco: f : R n R x opt = arg min x D f(x) (2.1) Działanie algorytmów ewolucyjnych polega na przetwarzaniu populacji P, w której każdy osobnik jest potencjalnym rozwiazaniem rozpatrywanego zadania. Na potrzeby niniejszej pracy, osobnika można utożsamiać z punktem w zbiorze dopuszczalnym D, a wartość funkcji celu w tym punkcie z jego przystosowaniem. Działanie algorytmu ewolucyjnego, po poczatkowej inicjalizacji populacji osobników (najczęściej poprzez losowanie z rozkładu jednostajnego w całej przestrzeni poszukiwań), opiera się na realizowaniu schematu działania pokazanego na rysunku 2.1. Odbywa się aż do spełnienia kryterium zatrzymania, np. znalezienia zadowalajacego przybliżenia punktu optymalnego x opt lub przekroczenia czasu działania algorytmu. Następujace po sobie operacje selekcji, krzyżowania oraz mutacji powoduja utworzenie nowych osobników na podstawie tych już istniejacych w populacji. Operator selekcji odpowiada za wybór osobników,

2.1. Algorytmy ewolucyjne 7 Inicjalizacja Algorytm ewolucyjny Środowisko Sukcesja Selekcja Ocena Operacje genetyczne Rysunek 2.1. Schemat algorytmu ewolucyjnego. Rysunek na podstawie rysunku nr 1 z pozycji [1]. które wykorzystywane sa jako argumenty operatorów genetycznych, a operacja sukcesji decyduje, które z punktów znajda się w nowej populacji. Ogólna idea algorytmu ewolucyjnego zaprezentowana jest w algorytmie 2.1. t 0 P t inicjalizacja() while nie jest spełniony warunek końca do if zachodzi krzyżowanie then Y t krzyżowanie(selekcja(p t )) V t mutacja(y t ) else V t mutacja(selekcja(p t )) end if P t+1 sukcesja(p t, V t ) t t + 1 end while Algorytm 2.1: Klasyczna postać algorytmu ewolucyjnego. 2.1.1. Metody selekcji Dobór metod wykorzystywanych w procesie selekcji jest istotny ze względu na zapewnienie zbieżności algorytmu. Poniżej opisane sa niektóre ze stosowanych wersji tych operatorów:

2.1. Algorytmy ewolucyjne 8 proporcjonalna (ruletkowa) - polega na zdefiniowaniu dla każdego osobnika populacji P t zmiennej losowej, która określa prawdopodobieństwo jego wyboru zgodnie ze wzorem: p r (P t i ) = f(p i t µ ) (2.2) f(pk t) gdzie f(pi t ) > 0 i f jest maksymalizowan a funkcj a celu. W danej metodzie, im lepsze jest przystosowanie osobnika, tym większa szansa, że zostanie on wybrany. k=1 rankingowa - osobniki populacji sa poszeregowane według wartości funkcji celu. Następnie każdemu z nich przydzielana jest ranga r (np. indeks w posortowanej populacji). Prawdopodobieństwo wyboru danego osobnika jest wtedy zależne od przypisanej mu rangi, zgodnie z określonymi przez twórcę algorytmu regułami. turniejowa - najpierw z populacji P wybierana jest grupa osobników Q P, przy czym każda kombinacja jest jednakowo prawdopodobna. Następnie z utworzonej grupy wybiera się osobnika o najlepszym przystosowaniu, który przechodzi do następnej populacji. progowa - jest szczególnym przypadkiem selekcji rankingowej - funkcja p r (P t i ) ma postać progu i określona jest wzorem: p r (P t i ) = { 1 ρµ, dla 0 r(p t i ) < ρµ 0, w przeciwnym przypadku (2.3) gdzie ρ jest wskaźnikiem nacisku selektywnego. redukuje liczbę osobników, które będa reprodukować. 2.1.2. Operator krzyżowania Zmniejszanie tej wartości Jest to operator w przestrzeni R n, którego działanie powoduje wymianę genów pomiędzy osobnikami. W algorytmach ewolucyjnych często przyjmuje się, że w krzyżowaniu biora udział dwa różne osobniki rodzicielskie x oraz u, w wyniku czego otrzymuje się jeden osobnik potomny y. W tej wersji operator krzyżowania oznaczany jest jako C(x, u) y. Najbardziej popularne sa krzyżowanie uśredniajace, które ma postać: y = (x + u)/2 (2.4) lub interpolacja: y = α u + (1 α) x (2.5) dla losowego α U(0, 1).

2.2. Klasyczna wersja algorytmu ewolucji różnicowej 9 2.1.3. Operator mutacji Jest to operator w przestrzeni R n, którego działanie polega na zmianie każdego genu osobnika w sposób losowy, co powoduje przesunięcie punktu we wszystkich wymiarach przestrzeni poszukiwań. Jest to tożsame z wylosowaniem punktu z sasiedztwa punktu mutujacego i ma postać: gdzie m i N(0, σ 2 ). 2.1.4. Metody sukcesji v j = y j + m j, j {1,.., n} (2.6) Operator sukcesji ma bezpośredni wpływ na jakość osobników, które przejda do następnej populacji. Często spotykane metody sukcesji to: z całkowitym zastępowaniem - do następnej populacji przechodzi wyłacznie populacja potomna. z częściowym zastępowaniem - w następnej populacji znajda się osobniki zarówno z populacji rodziców, jak i z potomnej, a reguły decydowania wybierane sa przez twórcę algorytmu. elitarna - następna populacja składa się z części najlepszych osobników z populacji rodziców oraz populacji potomnej. najlepiej przystosowanego osobnika z populacji rodziców. Gwarantuje to przetrwanie 2.2. Klasyczna wersja algorytmu ewolucji różnicowej Podstawowa wersja algorytmu ewolucji różnicowej została zaproponowana w pozycji [3] i jest zaprezentowana w algorytmie 2.2. Schemat oraz operatory genetyczne sa zmodyfikowane w porównaniu do klasycznego algorytmu ewolucyjnego pokazanego w algorytmie 2.1. t 0 P t inicjalizacja() while nie jest spełniony warunek końca do for i 1..µ do x selekcja(p t ) v mutacja_różnicowa(x, P t ) y krzyżowanie_wymieniajace(p i t, v) P t+1 i sukcesja_elitarna(pi t, y) end for t t + 1 end while Algorytm 2.2: Algorytm ewolucji różnicowej w klasycznej postaci. Operatory pokazane w algorytmie 2.2 zdefiniowane sa następujaco:

2.3. Warianty algorytmu ewolucji różnicowej 10 inicjalizacja() - losowanie wszystkich współrzędnych każdego osobnika w populacji z rozkładu jednostajnego w zbiorze dopuszczalnym. selekcja(p t ) - wybór punktu mutowanego x. W podstawowej wersji algorytmu losowana jest liczba z rozkładu jednostajnego w przedziale {1..µ}, która jest indeksem tego osobnika. mutacja_różnicowa(x, P t ) - poczatkowo wybierane sa z rozkładu jednostajnego dwa indeksy j oraz k, różne od siebie oraz od indeksu osobnika x. Osobnik zmutowany v wyznacza się poprzez sumę osobnika x oraz wektora różnicowego osobników P t j i P t k pomnożonego przez współczynnik skaluj acy F. v = x + F (P t j P t k ) (2.7) krzyżowanie_wymieniajace(p t, v) - osobnik powstały w wyniku mutacji bierze udział w krzyżowaniu wymieniajacym z osobnikiem P t Definicja w przypadku algorytmu ewolucji różnicowej ma postać: i i. { vj, z prawdopodobieństwem CR y j = Pi,j t, z prawdopodobieństwem 1 CR (2.8) Krzyżowanie to nazywane jest inaczej krzyżowaniem dwumianowym (ang. binomial crossover). sukcesja_elitarna(pi t, y) - wybór osobnika o lepszym przystosowaniu spośród produktu krzyżowania oraz aktualnie przetwarzanego osobnika z populacji P t. P t+1 i = { y, jeżeli f(y) < f(p t i ) P t i, w przeciwnym przypadku (2.9) Przebieg jednej iteracji algorytmu dla jednego osobnika zaprezentowany jest na rysunku 2.2. 2.3. Warianty algorytmu ewolucji różnicowej Klasyczna postać algorytmu nie jest jedyna skuteczna wersja algorytmu ewolucji różnicowej. Łatwość wydzielenia elementów składowych pozwala w prosty sposób na modyfikacje algorytmu. Doprowadziło to do powstania notacji, która jednoznacznie klasyfikuje możliwe warianty algorytmu ewolucji różnicowej: DE/x/y/z x - oznacza osobnika, który jest wybierany poprzez operator selekcji. Możliwe warianty to: rand - losowy wybór osobnika z całej populacji best - wybór osobnika o najlepszym przystosowaniu

2.3. Warianty algorytmu ewolucji różnicowej 11 1) Wybór przetwarzanego punktu. 2) Selekcja osobnika mutowanego oraz losowy wybór bez powtórze dwóch osobników do wektora ró nicowego. o 1 o 2 o 3 o 4 o n-1 o n przetwarzany osobnik + + F + - Populacja P t 4) Zsumowanie osobnika mutowanego i wektora ró nicowego 3) Obliczenie wektora ró nicowego i jego przeskalowanie v 1 v 2 v 3 v 4 v n-1 v n Osobniki zmutowane Krzy owanie dwumianowe y 1 Sukcesja elitarna Populacja P t+1 o 1 o 2 o 3 o 4 o n-1 o n Rysunek 2.2. Przebieg jednej iteracji algorytmu ewolucji różnicowej dla jednego osobnika. Rysunek wykonany na podstawie rysunku zawartego w [2].

2.4. Modyfikacje algorytmu 12 current - wybór osobnika bieżacego Pi t y - liczba różnic wektorów wykorzystywanych w mutacji z - schemat krzyżowania, np: dwumianowe, wykładnicze (ang. exponential). Metoda krzyżowania wykładniczego została przedstawiona w algorytmie 2.3. j 1 while j n do if a < CR then y j v j else break end if j j + 1 end while while j n do y j Pi,j t j j + 1 end while a jest zmienna losowa o rozkładzie jednostajnym w przedziale (0, 1). Algorytm 2.3: Operator krzyżowania wykładniczego. Tak przyjęte nazewnictwo pozwala na łatwe i szybkie zidentyfikowanie wykorzystywanej metody. Zgodnie z powyższa notacja, klasyczna postać algorytmu ewolucji różnicowej oznacza się jako DE/rand/1/bin. 2.4. Modyfikacje algorytmu Skuteczność algorytmu ewolucji różnicowej jest silnie zależna od wyboru jego parametrów oraz wariantu (notacja opisana w rozdziale 2.3), stad jego wykorzystanie w praktyce wymaga czasochłonnego dostosowania do rozpatrywanego problemu. Ponadto, klasyczna wersja algorytmu wykazuje często przedwczesna zbieżność populacji do punktu, który nie jest nawet minimum lokalnym rozpatrywanego problemu. W takim przypadku, nawet pojawianie się w populacji nowych osobników może nie zmienić sytuacji. W zwiazku z tym faktem, wielu naukowców podjęło próby wprowadzenia modyfikacji polegajacych na adaptacji parametrów w trakcie działania algorytmu. Przykładowe zmiany opisane w literaturze polegaja na: jde [5] - wprowadzeniu dla każdego osobnika populacji indywidualnych parametrów F oraz CR, które co generacja modyfikowane sa w sposób losowy. Lepsze wartości parametrów sa dzięki temu propagowane do następnych populacji, gdyż prowadza do powstawania bardziej przystosowanych osobników.

2.5. Wybrane zagadnienia 13 SaDE [6] - wykorzystywaniu dwóch strategii - DE/rand/1/bin oraz DE/current to best/2/bin odpowiednio z prawdopodobieństwem p oraz 1 p. Prawdopodobieństwo to jest wyznaczane co generacja na podstawie prowadzonych w trakcie działania algorytmu statystyk dotyczacych liczby osobników utworzonych dana strategia, które przeszły do następnej populacji. Parametr F jest indywidualny dla każdego osobnika i losowany jest z rozkładu normalnego: F i N(0.5, 0.3). CR zmieniane jest co ustalona liczbę generacji (w publikacji [6] co 5 generacji) poprzez losowanie z rozkładu N(0.5, 0.1). jde-2 [5] - wykorzystaniu trzech strategii - DE/rand/1/bin, DE/current to best/1/bin oraz DE/rand/2/bin. Wykorzystywane sa także dwa zestawy lokalnych wartości parametrów F oraz CR odpowiednio dla pierwszej i drugiej z wymienionych powyżej strategii. Dodatkowa modyfikacja jest także zastępowanie r najmniej przystosowanych osobników co s generacji losowymi osobnikami należacymi do zbioru dopuszczalnego D. W publikacji przyjęto parametry r = 1000 oraz s = 70 przy rozmiarze populacji µ = 200. JADE [7] - wprowadzeniu nowej wersji strategii nazwanej DE/current to pbest, gdzie generowanie wektora zmutowanego v i ma postać: v i = x i + F i (x p best x i) + F i (x r1 x r2 ) (2.10) gdzie x p best jest losowo wybranym osobnikiem spośród 100 p% najlepszych osobników populacji. Współczynnik p jest losowo wybierany z rozkładu jednostajnego w przedziale (0, 1], a F i jest współczynnikiem skalujacym zwiazanym z konkretnym osobnikiem x i. Modyfikacje parametrów F i oraz CR odbywaja się sposób losowy przy każdej następnej generacji. 2.5. Wybrane zagadnienia W poniższym rozdziale zostana omówione niektóre zagadnienia majace korzystny wpływ na jakość rozwiazań generowanych przez algorytm ewolucji różnicowej. 2.5.1. Środek geometryczny populacji Wartościa zalecana do monitorowania w trakcie działania algorytmu ewolucji różnicowej jest przystosowanie środka geometrycznego populacji. Weryfikacja empiryczna pokazała, że jego wartość funkcji celu często potrafi być znaczaco lepsza, niż najlepszego osobnika. Środek geometryczny nie jest brany pod uwagę w operatorach genetycznych algorytmu, a jego przystosowanie oblicza się analogicznie do zwykłych osobników populacji.

2.5. Wybrane zagadnienia 14 Środek geometryczny wyznacza się w następujacy sposób: P t mid = µ (P t i=1 µ i ) (2.11) 2.5.2. Kostkowe ograniczenia dziedziny Klasyczna wersja algorytmu ewolucji różnicowej pokazana w algorytmie 2.2 była zdefiniowana dla zadań optymalizacji bez ograniczeń. Jednak w większości problemów inżynierskich pojawiaja się dodatkowe ograniczenia, które musza być spełnione przez znalezione rozwiazania. Poprawny dobór metody naprawy punktów wykraczajacych poza zbiór dopuszczalny pozwala na zwiększenie różnorodności populacji i uniknięcie bładzenia losowego. Algorytm w wersji z naprawa punktów przedstawiony jest w algorytmie 2.4. t 0 P t inicjalizacja() while nie jest spełniony warunek końca do for i 1..µ do x selekcja(p t ) v mutacja_różnicowa(x, P t ) if v D then w v else w naprawa(v, x, P t ) end if y krzyżowanie_wymieniajace(p i t, w) P t+1 i sukcesja_elitarna(pi t, y) end for t t + 1 end while Algorytm 2.4: Algorytm ewolucji różnicowej z uwzględnieniem ograniczeń kostkowych. Wersja algorytmu 2.4 wprowadza jedna nowa operację naprawa(v, x, P t ), która jest wykonywana dla osobników zmutowanych v, które znajduja się poza zbiorem rozwiazań dopuszczalnych D. Poniżej znajduje się opis metod naprawy, które były zaprezentowane w publikacji [4]: Metoda konserwatywna (ang. conservatism) - osobnik w zastępowany jest osobnikiem x wyznaczonym przez operator selekcji. Reinicjalizacja (ang. reinitialization) - współrzędne osobnika w sa ponownie losowane z rozkładu jednostajnego w przedziale [l, u].

2.5. Wybrane zagadnienia 15 Odbicie (ang. reflection) - osobnik w generowany jest poprzez odbicie współrzędnej od przekroczonej granicy l j lub u j. v j, jeżeli l j v j u j w j = 2 u j v j, jeżeli v j > u j (2.12) 2 l j v j, jeżeli v j < l j Rzutowanie (ang. projection) - wszystkie współrzędne, które przekroczyły odpowiadajace granice, sa zrzutowane do tych granic. v j, jeżeli l j v j u j w j = u j, jeżeli v j > u j (2.13) l j, jeżeli v j < l j Zawijanie (ang. wrapping) - wszystkie współrzędne, które przekroczyły odpowiadajace granice sa przesunięte przez odpowiednio dobrana, całkowita wielokrotność rozmiaru przedziału [l j, u j ]. { vj, jeżeli l j v j u j w j = v j + k j (u j l j ), w przeciwnym przypadku (2.14) gdzie k j jest liczba całkowita umożliwiajac a uzyskanie punktu dopuszczalnego. Ponowne próbkowanie (ang. resampling) - ponowienie operatorów selekcji i mutacji różnicowej dla populacji P t. w = mutacja_różnicowa(selekcja(p t ), P t ) Zorientowana wokół środka geometrycznego (ang. midpoint oriented) - naprawa polega na wyznaczaniu wartości v j poprzez połowienie odcinka pomiędzy x mid,j a v j do momentu, aż v j [l j, u j ]. Metoda pokazana jest w algorytmie 2.5. while v jest poza D do for j 1..n do v j 0.5 (v j + x mid,j ) end for end while w v Algorytm 2.5: Metoda uwzględnienia ograniczeń kostkowych zorientowana wokół środka geometrycznego populacji. Przykładowa naprawa punktu algorytmem 2.5 dla trójwymiarowej przestrzeni pokazana jest na rysunku 2.3. Osobnik oznaczony kolorem zielonym jest wynikiem pierwszej iteracji i jest poza zbiorem dopuszczalnym D. Osobnik zaznaczony kolorem niebieskim jest rezultatem drugiej iteracji i jest finalnym wynikiem działania tej metody naprawy.

2.5. Wybrane zagadnienia 16 Rysunek 2.3. Naprawa osobnika zmutowanego metoda zorientowana wokół punktu środkowego.

3. Programowalne karty graficzne Idea programowalnych kart graficznych (GPGPU) została zapoczatkowana w 2001 roku. Polegała ona na udostępnieniu programistom możliwości pisania własnych, krótkich programów wykonywanych przez jednostki cieniujace karty graficznej (ang. shaders), dla każdego wierzchołka i piksela grafiki trójwymiarowej. Ich możliwości były jednak bardzo ograniczone, a tworzone programy nie mogły przekraczać 128 instrukcji. Pomysł ten został jednak szybko uznany jako bardzo użyteczny nie tylko w grafice komputerowej, ale także w kontekście obliczeń niezwiazanych bezpośrednio z grafika komputerowa. Przyczyniło się to do stworzenia pierwszych kart graficznych, których możliwości nie kończyły się wyłacznie na wykonywaniu instrukcji typowych dla jednostek cieniujacych, ale rozkazów ogólnych, wymaganych do rozwiazywania typowych problemów numerycznych. Dalszy ich rozwój, napędzany głównie szybko rosnacymi wymaganiami rynku gier komputerowych, doprowadził do przekształcenia klasycznych kart graficznych w specjalizowane urzadzenia wielordzeniowe, które oferuja bardzo duża moc obliczeniowa, co umożliwiło powstanie heterogenicznych platform programistycznych. Dzięki heterogenicznym platformom programistycznym możliwy jest podział tworzonych aplikacji na fragmenty, które, w zależności od swojej specyfiki, wykonuja się na jednostce centralnej lub karcie graficznej. Na CPU, które składa się z kilku jednostek wykonawczych potrafiacych wykonywać nawet bardzo złożone rozkazy, trafiaja części wykonujace się sekwencyjnie. Do wykonania na GPU, zbudowanej z tysięcy mniej zaawansowanych rdzeni, oddelegowane sa te fragmenty kodu, w których możliwe jest zrównoleglenie obliczeń. Taka metodyka zaczyna być w dzisiejszych czasach coraz częściej wykorzystywana przez twórców oprogramowania komputerowego zarówno dla zwykłych użytkowników, jak i dedykowanego dla rozwiazań przemysłowych czy naukowych. Głównymi producentami programowalnych kart graficznych w momencie tworzenia niniejszej pracy jest AMD z rozwiazaniem AMD FireStream oraz NVI- DIA z technologia CUDA. W zwiazku z dominujac a pozycja drugiej z wymienionych firm w tym segmencie rynku, w pracy omówiona i wykorzystana została technologia CUDA.

3.1. NVIDIA CUDA 18 Rysunek 3.1. Skalowalność aplikacji pomiędzy kartami graficznymi wyposażonymi w różna liczbę multiprocesorów. Rysunek nr 5 w pozycji [13] 3.1. NVIDIA CUDA Technologia CUDA (Compute Unified Device Architecture) została po raz pierwszy zaprezentowana w listopadzie 2006 roku przez firmę NVIDIA. Opiera się ona na wielordzeniowych multiprocesorach graficznych i umożliwia ich wykorzystanie w celu rozwiazywania w sposób zrównoleglony problemów numerycznych. Środowisko programistyczne platformy CUDA oparte jest na języku C, a w jego skład wchodzi własny kompilator nvcc, debugger cuda-gdb, profiler (umożliwia sprawdzanie wykorzystania zasobów karty), a także interfejs programowania aplikacji. Z czasem utworzone zostały także rozszerzenia pozwalajace na tworzenie oprogramowania w innych językach, np. Java, Ruby, Lua. Python, Perl, Fortran, Platforma CUDA dostępna jest na wszystkich kartach od rodziny G8x włacz- nie, w tym modeli GeForce, Quadro oraz Tesla. Dzięki zapewnieniu pełnej kompatybilności wstecznej tej technologii, tworzone oprogramowanie jest w pełni przenośne na nowsze modele produktów firmy NVIDIA. Oferowana przez technologię CUDA skalowalność jest dzięki temu w pełni transparentna, gdyż wydajność tworzonych aplikacji będzie rosła wraz z liczba procesorów oferowanych przez kartę graficzna, bez żadnych ingerencji w kod źródłowy. Zobrazowane jest to na rysunku 3.1. W zwiazku z odmienna idea działania technologii CUDA w stosunku do standardowych aplikacji, można wskazać znaczne różnice w budowie multiprocesora graficznego w porównaniu do standardowej jednostki centralnej. W GPU większa

3.1. NVIDIA CUDA 19 Rysunek 3.2. Różnice w budowie multiprocesora graficznego i klasycznej jednostki centralnej. Rysunek nr 3 w pozycji [13]. liczba tranzystorów poświęcona jest na jednostki arytmetyczno - logiczne zajmujace się przetwarzaniem danych, a zatem mniejsza ich liczba wykorzystywana jest na sterowanie przepływem instrukcji czy pamięć podręczna. Powoduje to, że multiprocesory graficzne sa skuteczniejsze w algorytmach, w których bardzo duża ilość danych przetwarzana jest w ten sam sposób. Różnice w budowie klasycznej jednostki centralnej i multiprocesora graficznego pokazane sa na rysunku 3.2. Poniżej opisane zostana podstawowe pojęcia zwiazane z technologia CUDA, czyli jej model programowania oraz architektura wewnętrzna. Omówione będa także wybrane funkcjonalności tego środowiska. 3.1.1. Model programowania W poniższym podrozdziale omówione zostana podstawowe pojęcia, które składaja się na model programowania technologii CUDA. Jadro Jest to rozszerzenie języka C, w którym definiuje się funkcję, jaka będzie wykonywana przez watki na karcie graficznej. W odróżnieniu od standardowej funkcji języka programowania C, gdy jadro jest uruchamiane, powoduje to równoległe wykonanie zdefiniowanej w nim operacji N razy przez N różnych watków. Każdy z utworzonych watków otrzymuje unikalny numer, co pozwala na określenie podziału pracy pomiędzy watkami. Hierarchia watków Watki tworzone przy wywołaniu jadra sa logicznie zorganizowane w trójwymiarowe bloki. Sa w nich identyfikowane poprzez przypisane im współrzędne x, y oraz z. Pozwala to na łatwy podział obliczeń pomiędzy watkami przy przetwarzaniu podstawowych jednostek matematycznych, takich jak wektory czy macierze. Bloki z kolei sa zgrupowane w jedno, dwu lub trójwymiarowa siatkę, a indeksowanie bloków w niej jest analogiczne jak w przypadku watków. Oprócz indeksów

3.1. NVIDIA CUDA 20 Rysunek 3.3. Przykład hierarchi watków składajacy się z dwuwymiarowej siatki zawierajacej dwuwymiarowe bloki watków. Rysunek nr 6 w pozycji [13]. identyfikujacych watek i blok, dostępne sa zmienne pozwalajace na odczyt rozmiaru bloku oraz siatki. Liczba watków w bloku oraz liczba bloków w siatce określana jest w rozszerzeniu języka C przy wywoływaniu jadra. Przykład dwuwymiarowej siatki (o rozmiarze 3 2), na która składaja się dwuwymiarowe bloki watków (4 3), pokazany jest na rysunku 3.3. Hierarchia pamięci Pamięć dostępna na karcie graficznej, podobnie jak w zwykłym systemie komputerowym, jest hierarchiczna, a watki w trakcie swojego działania moga wykonywać dostęp do różnych jej rodzajów. Każdy z nich ma swoja własna pamięć lokalna, dostępna wyłacznie dla niego. Watki zgrupowane w blok moga odwoływać się do tej samej pamięci współdzielonej, dostępnej w trakcie wykonywania się tego bloku. Wszystkie watki (niezależnie od ich umiejscowienia w bloku i siatce) moga operować na tej samej pamięci globalnej. Dostępna dla nich jest także pamięć stała (wyłacznie do odczytu), do której odwołania sa optymalizowane.

3.1. NVIDIA CUDA 21 Zawartość tej pamięci pozostaje niezmieniona pomiędzy wywołaniami wszystkich jader wchodzacych w skład aplikacji. Zgodnie z regułami pojęcia hierarchii pamięci, operowanie na pamięci globalnej dostępnej dla wszystkich watków jest najwolniejsze. W trakcie tworzenia aplikacji zaleca się minimalizację jej użycia na rzecz pamięci współdzielonej. Dostęp do niej jest szybszy ze względu na to, że jest to pamięć wewnętrzna multiprocesora. Najbardziej efektywne jest używanie pamięci lokalnej watku, która lokowana jest w rejestrach multiprocesora. Programowanie heterogeniczne Głównym założeniem modelu programowania opracowanego dla technologii CUDA jest wykonanie się watków utworzonych przy wywołaniu jadra na karcie graficznej, a pozostałych operacji na jednostce centralnej. Karta graficzna jest jednak urzadzeniem fizycznie odseparowanym od jednostki centralnej, w zwiazku z tym operuje na osobnej przestrzeni adresowej. Przekazywanie danych do watków karty graficznej wymaga zatem alokacji i dealokacji pamięci w jej zasobach, jak również transferu danych pomiędzy pamięciami urzadzenia podrzędnego i nadrzędnego. 3.1.2. Architektura wewnętrzna W budowie karty graficznej firmy NVIDIA, w zależności od rodziny i modelu karty, wyróżnić można wiele procesorów graficznych (nazwanych SM - Streaming Multiprocessor). Ich unikalna architektura umożliwiajaca równoległe wykonanie watków została nazwana SIMT - Single Instruction, Multiple-Thread, co jest nawiazaniem do architektury systemów komputerowych SIMD - Single Instruction, Multiple Data, wyróżnionej w taksonomii Flynna. Architektura SIMT pozostaje na wyższym poziomie abstrakcji, gdyż wymaga zdefiniowania zachowania pojedynczego watku, o wykonaniu którego w pełni decyduje multiprocesor. W architekturze SIMD wymagane jest natomiast samodzielne grupowanie danych w wektory, których wielkość jest określona przez architekturę. Kiedy multiprocesorowi przydzielony jest do wykonania blok, ten dzieli go na grupy watków po 32 każda, które nazywane sa osnowami. Ich szeregowaniem zarzadza planista, który wybiera spośród gotowych do wykonania osnów jedna, której zostanie przydzielony czas multiprocesora. Z każdym taktem zegara multiprocesor wykonuje jedna instrukcję dla każdego watku w osnowie. W przypadku zaistnienia rozejścia w kodzie programu, każda z występujacych ścieżek wykonania realizowana jest sekwencyjnie. W trakcie realizowania kolejnych rozejść warunkowych, instrukcje wykonuja się tylko dla watków podażaj acych aktualnie rozpatrywana ścieżka wykonania programu. Po rozpatrzeniu wszystkich rozejść warunkowych, dalsze

3.1. NVIDIA CUDA 22 podażanie wszystkich watków jedna ścieżka wykonania odbędzie się po ich synchronizacji. Największa wydajność działania można zatem uzyskać poprzez minimalizację liczby rozejść warunkowych w programie. Każdy z watków w osnowie zaczyna działanie spod tego samego adresu programu, ale z własnym rejestrem stanu oraz licznikiem rozkazów, które składaja się na kontekst osnowy i przechowywane sa w pamięci wewnętrznej multiprocesora. W zwiazku z tym planista wybiera gotowa do wykonania kolejnej instrukcji osnowę co takt procesora, gdyż koszt przełaczenia kontekstu jest niewielki. Do każdego multiprocesora może w danym momencie być przypisana ograniczona liczba bloków oraz osnów, która zależna jest od wielkości pamięci wewnętrznej dostępnej w pojedynczym multiprocesorze graficznym. 3.1.3. Wybrane funkcjonalności W poniższym podrozdziale zostana omówione te funkcjonalności oferowane przez technologię CUDA, które były uznane za najbardziej przydatne w kontekście powstałego w ramach niniejszej pracy oprogramowania. Mapowanie pamięci niepodlegajacej wymianie stron Środowisko wykonawcze pozwala na zaalokowanie po stronie urzadzenia nadrzędnego pamięci niepodlegajacej wymianie, która w trakcie operacji wymiany stron przez system operacyjny nie zostanie wymieciona z pamięci operacyjnej do pamięci masowej. Operacje kopiowania (w obu kierunkach) pomiędzy pamięcia niepodlegajac a wymianie stron, a pamięcia urzadzenia podrzędnego, moga być zatem wykonywane równolegle z aktualnie działajacym jadrem. Istnieje także możliwość zmapowania pamięci niepodlegajacej wymianie w przestrzeni pamięci urzadzenia podrzędnego. Nie ma wtedy konieczności alokowania analogicznych buforów po stronie urzadzenia podrzędnego i nadrzędnego, gdyż kopiowanie danych odbywa się wtedy niejawnie (czym zarzadza środowisko wykonawcze CUDA) i może być wykonywane równolegle z działajacym na karcie jadrem. Zaalokowanie zbyt dużej ilości pamięci niepodlegajacej wymianie stron może jednak doprowadzić do znacznego ograniczenia wydajności systemu komputerowego, która wynikałaby z konieczności częstej wymiany stron. Instrukcje atomowe Instrukcje atomowe powoduja wykonanie operacji odczytu, modyfikacji i zapisu 32 lub 64-bitowego słowa w pamięci współdzielonej albo globalnej w sposób atomowy. Pozwala to na operowanie wieloma watkami na tych samych fragmentach pamięci bez ryzyka zaistnienia zjawiska hazardu.

3.2. Implementacje DE na GPGPU 23 3.2. Implementacje DE na GPGPU Algorytm ewolucji różnicowej, ze względu na swój charakter, stał się tematem analizowanym pod katem możliwości przyspieszania czasu wykonania poprzez zrównoleglanie operacji na karcie graficznej. Niektóre publikacje wykazuja znaczny wzrost wydajności działania implementacji wykonanej z wykorzystaniem technologii CUDA, w porównaniu do użycia wyłacznie jednostki centralnej. W pozycji [8] zaprezentowana została zmodyfikowana wersja algorytmu ewolucji różnicowej. Zastosowano w niej dodatkowo metodę poszukiwania wzorca, co miało umożliwić uniknięcie stagnacji w działaniu algorytmu. Testy stworzonej aplikacji zostały wykonane na karcie graficznej GeForce GTX280, wyposażonej w 30 procesorów graficznych, po 8 rdzeni każdy. Pozwoliło to na uzyskanie nawet 7-krotnego przyspieszenia w porównaniu do zwykłej, jednowatkowej aplikacji. Autor tej publikacji uważa też, że jest to pierwsza zaprezentowana implementacja realizujaca algorytm DE na karcie graficznej. Testy pod katem wzrostu wydajności implementacji na GPU w zależności od wielkości populacji (przy jednakowej liczbie generacji), zostały zaprezentowane w pozycjach [9] oraz [10]. W pierwszej z nich wykorzystana była karta graficzna GeForce GTX285, wyposażona w 30 procesorów graficznych po 8 rdzeni każdy. Dla populacji 100 oraz 1000 osobników uzyskano przyspieszenia odpowiednio do 19.04 oraz do 35.48 razy. W drugiej z wymienionych pozycji użyto karty GeForce GTX260 (192 rdzenie). Wykonana implementacja, dla tych samych rozmiarów populacji 100 oraz 1000 dała przyspieszenia odpowiednio do 2.85 i 14.80 razy. W pozycji [11], gdzie algorytm utworzony był z wykorzystaniem zmodyfikowanego środowiska EASEA (opisanego w rozdziale 4), testowane było możliwe przyspieszenie w zależności od rozmiaru populacji, przy różnych funkcjach celu. Zaprezentowane zostały wyniki dla dwóch z nich. Stworzona implementacja, uruchomiona na karcie graficznej GeForce GTX480 (480 rdzeni), pozwoliła na uzyskanie przyspieszenia ok. 10 razy dla pierwszej funkcji celu oraz ok. 5 razy dla drugiej, przy rozmiarze populacji wynoszacym 10000.

4. Implementacja algorytmu DE w platformie EASEA EASEA to platforma stworzona przez SONIC (Stochastic Optimisation and Nature Inspired Computing) - grupę zespołu BFO z Uniwersytetu w Strasburgu. Pierwsza wersja oprogramowania została stworzona w 1998 roku i opierała się o zewnętrzne biblioteki GALib ( C++ Library of Genetic Algorithm Components) albo EO (Evolving Objects), które definiowały podstawowe byty wykorzystywane przez algorytmy ewolucyjne. Platforma była dostępna na systemy operacyjne zgodne ze standardem POSIX (Windows, Linux, Mac OS). Stworzenie EASEA możliwe było dzięki wyodrębnieniu cech wspólnych wszystkich algorytmów ewolucyjnych i utworzeniu jednego algorytmu z wieloma parametrami, których odpowiednie ustawienie umożliwia wybór pomiędzy konkretnymi rozwiazaniami. Rozwój pierwszej wersji platformy EASEA zakończył się w 2003 roku, ale mimo to cieszyła się ona popularnościa jeszcze przez kilka lat. Wznowienie prac nastapiło w 2008 roku. W zwiazku z licznościa stworzonych na platformie EASEA rozwiazań, jednym z podstawowych wymogów w trakcie prac nad nowa wersja było zachowanie zgodności wstecz z poprzednia. Głównym przedsięwzięciem była taka modyfikacja platformy EASEA, aby wspierała ona generowanie aplikacji możliwych do zrównoleglonego wykonywania na komputerach wyposażonych w karty graficzne firmy NVIDIA. Kolejna dodana możliwościa było zrównoleglenie wygenerowanej aplikacji na komputerach połaczo- nych w jedna sieć komputerowa (ang. remote island model). Aktualna wersja oprogramowania pozwala także na serializację i deserializację populacji, automatyczne tworzenie wykresów programem Gnuplot, generowanie skryptów w języku R, będacym środowiskiem do obliczeń statystycznych. Główna idea stworzenia platformy EASEA było umożliwienie osobom z podstawowa wiedza informatyczna stworzenie zrównoleglonej na wielordzeniowych platformach aplikacji, która pozwala na rozwiazywanie złożonych, rzeczywistych problemów przy użyciu algorytmów ewolucyjnych. Wygenerowane rozwiazanie pozwalało na osiagnięcie znacznych przyspieszeń (nawet do 1000 razy wg pozycji [16]) w stosunku do standardowych, jednowatkowych aplikacji. Dostępna była możliwość rozwiazywania problemów następujacymi wersjami algorytmów ewolucyjnych: GP (Genetic Programming), CMA-ES (Covariance Matrix Adaptation Evolution Strategy) oraz MA (Memetic Algorithm).

4.1. Struktura platformy EASEA 25 4.1. Struktura platformy EASEA W strukturze oprogramowania EASEA można wyróżnić cztery istotne części: bibliotekę LibEASEA, pliki szablonowe (rozszerzenie tpl), pliki konfiguracyjne (rozszerzenie ez) uzupełniane przez użytkownika oraz generator kodu. Doła- czony jest także statycznie skompilowany moduł o nazwie program options stanowiacy fragment biblioteki boost, który wykorzystywany jest do dynamicznego odczytywania parametrów w trakcie działania aplikacji. Pracę ze środowiskiem EASEA ułatwiaja załaczone do niego przykłady. 4.1.1. Biblioteka LibEASEA W nowej wersji oprogramowania wspieranej po 2008 roku zrezygnowano z zamiennego stosowania zewnętrznych bibliotek GALib oraz EO, co wymagało utworzenia własnej biblioteki, która definiowała niezbędne w każdym algorytmie ewolucyjnym byty. Najistotniejszymi z punktu widzenia algorytmów ewolucyjnych klasami biblioteki LibEASEA utworzonej w języku C++ sa: CIndividual - klasa abstrakcyjna reprezentujaca pojedynczego osobnika populacji. CPopulation - odpowiada utrzymywanej w trakcie działania algorytmu populacji. CEvolutionaryAlgorithm - reprezentuje algorytm ewolucyjny oraz definiuje pętlę główna algorytmu. CRandomGenerator - odpowiada wykorzystywanemu w trakcie działania algorytmu ewolucyjnego generatorowi liczb losowych. Korzysta z implementacji generatora Mersenne Twister. Parameters - klasa zawierajaca parametry algorytmu ewolucyjnego. CSelectionOperator - klasa abstrakcyjna będaca klasa bazowa dla implementacji operatorów selekcji. CStoppingCriterion - reprezentuje kryterium końca pętli głównej algorytmu. Wymienione wyżej klasy sa ogólna reprezentacja bytów występujacych w każdym algorytmie ewolucyjnym. W bibliotece zawarte sa także klasy definiujace szczególne atrybuty wykorzystywane przez te rodziny algorytmów ewolucyjnych, które sa wspierane przez platformę EASEA. Pętla główna algorytmu ewolucyjnego Biblioteka LibEASEA definiuje pętlę główna algorytmu ewolucyjnego, która odpowiada działaniu algorytmu. Jest ona elastyczna, gdyż kolejne jej kroki sa ściśle zależne od zdefiniowanych w klasie Parameters parametrów. Schemat blokowy pętli głównej algorytmu ewolucyjnego w bibliotece LibEASEA pokazany jest na rysunku 4.1. Żółte pola symbolizuja operatory definiowane przez użytkownika wymagane do prawidłowej pracy algorytmu ewolucyjnego. Zielone bloki

4.1. Struktura platformy EASEA 26 Obliczenie przystosowania rodziców Inicjalizacja populacji Przed zacz ciem dzia ania Na ko cu generacji Populacja rodziców Warunek ko ca Po sko czeniu dzia ania Przed redukcj populacji Redukcja populacji Generacja Na pocz tku generacji Populacja rodzice oraz potomstwo Selekcja rodziców Obliczenie przystosowania potomstwa Mutacja i krzy owanie Rysunek 4.1. Schemat działania głównej pętli algorytmu ewolucyjnego w bibliotece LibEASEA. Rysunek wykonany na podstawie diagramu przedstawionego w pozycji [17]. symbolizuja operacje, w których moga być zawarte specyficzne działania dla niektórych rodzin algorytmów. Moga być także wykorzystywane do wprowadzania modyfikacji i testowania zachowania zmienionego algorytmu. Niebieskie bloki na diagramie symbolizuja populację i określaja, w jakim znajduje się stanie. 4.1.2. Pliki konfiguracyjne Plik konfiguracyjny jest uzupełniany przez użytkownika w pseudokodzie zbliżonym do składni języka C++, a jego struktura jest ściśle określona. Na poczatku należy w nim zdefiniować, jak wyglada reprezentacja osobnika przy rozwiazywa- niu danego problemu (jego cechy oraz dotyczace go parametry). Możliwe jest także zawarcie tutaj innych, pobocznych klas, które będa wykorzystywane w danym algorytmie. Definicje wprowadza się w sekcji przedstawionej na wydruku 4.1. W zwiazku z tym, że struktura osobnika jest ustalana przez użytkownika, udostępniona jest także opcja własnego określenia sposobu inicjalizacji osobnika, która definiowana jest w sekcji pokazanej na wydruku 4.2.

4.1. Struktura platformy EASEA 27 1 \User classes : GenomeClass { // d e f i n i c j a osobnika 5 } \end Wydruk 4.1. Sekcja definiowania reprezentacji osobnika w pliku konfiguracyjnym. 1 \GenomeClass : : i n i t i a l i s e r : // d e f i n i c j a i n i c j a l i z a c j i osobnika \end Wydruk 4.2. Sekcja inicjalizacji osobnika w pliku konfiguracyjnym. Zgodnie z diagramem na rysunku 4.1, konieczne jest także podanie definicji operatora krzyżowania, która umieszcza się w sekcji widocznej na wydruku 4.3. Dostęp do dwóch krzyżujacych osobników możliwy jest poprzez zmienne parent1 oraz parent2, natomiast do potomka (poczatkowo kopii osobnika dostępnego jako parent1) przez zmienna child. 1 \GenomeClass : : crossover : // d e f i n i c j a operatora krzyzowania // dostep do cech osobnikow krzyzujacych poprzez zmienne parent1, parent2 // dostep do cech osobnika potomnego poprzez zmienna child 5 \end Wydruk 4.3. Sekcja definicji operatora krzyżowania w pliku konfiguracyjnym. Operator mutacji definiowany jest w sekcji pokazanej na wydruku 4.4. Możliwy jest w niej dostęp do cech osobnika mutujacego poprzez zmienna Genome. W sekcjach widocznych na wydrukach 4.5 oraz 4.6 należy zdefiniować postać funkcji celu i opcjonalnie własne zmienne lub funkcje globalne. Pozostałe bloki diagramu 4.1 sa opcjonalne i w sekcjach zwiazanych z nimi implementujemy działanie algorytmu ewolucyjnego: przed inicjalizacja populacji, przed każda generacja, przed każda redukcja populacji, po każdej generacji, po zakończeniu algorytmu (spełnieniu kryterium końca). Powyżej opisane sekcje sa widoczne na wydruku 4.7. Na końcu plik konfiguracyjnego występuje także sekcja dotyczac a definiowania parametrów wykonawczych algorytmu, takich jak liczba generacji, ograniczenie czasowe wykonania algorytmu, rozmiar populacji rodziców oraz dzieci.

4.1. Struktura platformy EASEA 28 1 \GenomeClass : : mutator : // d e f i n i c j a operatora mutacji // dostep do cech osobnika mutujacego poprzez Genome[ i ] // i j e s t indeksem cechy 5 \end Wydruk 4.4. Sekcja definicji operatora mutacji w pliku konfiguracyjnym. 1 \GenomeClass : : evaluator : // d e f i n i c j a funkcji celu \end Wydruk 4.5. Sekcja definicji funkcji celu w pliku konfiguracyjnym. Pełna struktura pliku konfiguracyjnego jest możliwa do znalezienia w pozycji [16] w sekcji Starting with EASEA. 4.1.3. Pliki szablonowe Pliki szablonowe platformy EASEA zawieraja szkielet kodu algorytmu ewolucyjnego. Sa one napisane w języku C++ z dodatkowymi identyfikatorami wykorzystywanymi przez generator kodu. Budowa szkieletu algorytmu ewolucyjnego w pliku szablonowym opiera się na wykorzystaniu klas biblioteki LibEASEA opisanych w rozdziale 4.1.1 oraz mechanizmu dziedziczenia języka C++. Okazuje się to rozwiazaniem elastycznym, które pozwala na realizację wielu różnych rodzin algorytmów w taki sam sposób. W aktualnej wersji oprogramowania (w momencie tworzenia pracy jest to wersja oznaczona numerem 1.0.2) dostępne sa szablony pozwalajace na wygenerowanie kodu klasycznego algorytmu ewolucyjnego, GP, CMA-ES oraz MA zarówno w wersji liczacej wartość funkcji celu na jednostce centralnej, jak i na GPU. 4.1.4. Generator kodu Generacja kodu odbywa się poprzez analizę wybranego z linii poleceń pliku szablonowego, który zostanie dostosowany zgodnie z wypełnionymi w pliku konfiguracyjnym operatorami i parametrami. Występujace w szablonie identyfikatory sa usuwane i zastępowane wynikiem działania generatora kodu, na który składa się działanie wygenerowanego narzędziem ALex leksera oraz utworzonego oprogramowaniem AYACC parsera. Lekser analizuje plik konfiguracyjny i dopasowuje otrzymany strumień znaków do zdefiniowanych przez wyrażenia regularne wzorców, na podstawie których definiuje symbole leksykalne. Opatrzone sa one kodem źródłowym w języku C, który określa wykonywane czynności w sytuacji znalezienia takiego symbolu

4.2. Modyfikacje platformy EASEA 29 1 \User declarations : // deklaracja i d e f i n i c j a wlasnych zmiennych lub funkcji globalnych \end Wydruk 4.6. Sekcja definicji własnych zmiennych lub funkcji w pliku konfiguracyjnym. 1 \Before everything else function : //cout <<" Before everything else function called "<<endl ; \end 5 \ After everything else function : //cout << " After everything else function called " << endl ; \end \At the beginning of each generation function : 10 //cout << " At the beginning of each generation function called " << endl ; \end \At the end of each generation function : //cout << " At the end of each generation function called " << endl ; 15 \end \At each generation before reduce function : //cout << " At each generation before replacement function called " << endl ; \end Wydruk 4.7. Sekcja definicji zachowania algorytmu w szczególnych momentach w pliku konfiguracyjnym. podczas analizy pliku szablonowego. W przypadku platformy EASEA jest to zastapienie znalezionego symbolu odpowiadajacym mu kodem źródłowym badź parametrem, których definicje stworzone sa przez użytkownika w pliku konfiguracyjnym. Utworzone przez lekser symbole leksykalne odpowiadajace zdefiniowanym parametrom algorytmu maja swoje odpowiedniki w postaci specjalnych elementów parsera zwanych tokenami. Z każdym tokenem powiazany jest kod w języku C, który odpowiada za pobranie wartości odpowiedniego parametru z pliku konfiguracyjnego. Odczytane przez parser wartości sa wykorzystywane przez lekser i wstawiane do generowanego kodu. Proces generacji kodu w platformie EASEA prezentuje rysunek 4.2. 4.2. Modyfikacje platformy EASEA Podstawowym założeniem przy rozszerzeniu EASEA o algorytm ewolucji różnicowej była możliwie najmniejsza ingerencja w wewnętrzna strukturę biblioteki

4.2. Modyfikacje platformy EASEA 30 Szablon Plik kon guracyjny Generator kodu Wygenerowany kod mutacja Generacja kodu krzy owanie Generacja kodu parametry Generacja kodu funkcja celu Generacja kodu Analiza szablonu Rysunek 4.2. Proces generacji kodu w platformie EASEA. Rysunek wykonany na podstawie rysunku zawartego w pozycji [17]. LibEASEA, a dostosowanie i utworzenie niezbędnych mechanizmów bezpośrednio w pliku szablonowym. Pozwoliło to na zminimalizowanie możliwych konfliktów z dostępnymi już w platformie rozwiazaniami. Prace doprowadziły do powstania plików szablonowych pozwalajacych na wygenerowanie kodu aplikacji wykorzystujacego algorytm ewolucji różnicowej w wersjach z obliczaniem funkcji celu zarówno na CPU (plik STD_DE.tpl) jak i GPU (plik CUDA_DE.tpl). 4.2.1. Rozszerzenia generatora kodu Algorytm ewolucji różnicowej ma swoje specyficzne parametry oraz kroki algorytmu, które nie były dostępne w oryginalnej platformie EASEA. Należało zatem dodać do platformy EASEA nowe symbole możliwe do wykorzystywania w plikach szablonowych oraz zdefiniować zachowanie generatora kodu przy ich obsłudze. Wymagało to dokonania zmian w pliku reguł leksera EaseaLex.l oraz pliku reguł analizatora składni EaseaParse.y. Symbole zastępowane wartościami zdefiniowanych parametrów W konfguracji leksera umieszczono reguły w postaci wyrażeń regularnych, po których w pliku konfiguracyjnym występuja wartości parametrów. Zmiany pokazane sa na wydruku 4.8. Kolejnym krokiem było zdefinowanie zachowania leksera w momencie napotkania takiego symbolu przy analizie pliku szablonowego. Pożadanym działaniem była wtedy zamiana w generowanym kodzie danego symbolu pewna zmienna. Modyfikacje zaprezentowane sa na wydruku 4.9. Za odczytanie wartości wstawianej w miejsce symbolu leksykalnego odpowiada parser. W jego regułach należało umieścić definicję tokenów odpowiadajacych symbolom, a także umieścić kod zapewniajacy odczytanie wartości parametru. Zobrazowane jest to na wydruku 4.10.

4.2. Modyfikacje platformy EASEA 31 1 //MS: adding an expression to parse in. ez f i l e <GET_PARAMETERS>" Scaling " [ \t\n] + " factor " [ \t\n ] " : " [ \t\n] { i f (bverbose) 5 p r i n t f ( " \ tscaling factor... \ n " ) ; return SCALING_FACTOR; }... <GET_PARAMETERS>" Crossover " [ \t\n] + " type " [ \t\n ] " : " [ \t\n] 10 { } 15 //MS: over i f (bverbose) p r i n t f ( " \ tcrossover type... \ n " ) ; return CROSSOVER_TYPE; Wydruk 4.8. Reguły leksera umożliwiajace wykrywanie nowych parametrów w pliku konfiguracyjnym. 1 //MS: replacing symbol with code that writes to a result f i l e <TEMPLATE_ANALYSIS>"\\SCALING_FACTOR" { f p r i n t f ( fpoutputfile,"% f ",fscaling_factor ) ; 5 }... <TEMPLATE_ANALYSIS>"\\CROSSOVER_TYPE" { f p r i n t f ( fpoutputfile,"%d",ncrossover_type) ; 10 } //MS: over Wydruk 4.9. Zachowanie leksera przy napotkaniu symbolu nowego parametru. 1 // MS: global variables for D i f f e r e n t i a l Evolution state f l o a t fscaling_factor=0.5; i n t ncrossover_type=0;... 5 //MS: over //MS: DE tokens %token SCALING_FACTOR %token CROSSOVER_TYPE 10 //MS: over //MS: getting parameter values from configuration f i l e SCALING_FACTOR NUMBER2 { 15 fscaling_factor=( f l o a t ) $2 ; i f ( fscaling_factor < 0.0 f ) { f p r i n t f ( stderr, " \n%s Warning line %d : \ Scaling factor must be value in [ 0, 1 ]. \ 20 Value 0 inserted.\n. ", sez_file_name, EASEALexer. yylineno ) ; nwarnings++; fscaling_factor = 0.0 f ; } else i f ( fscaling_factor > 1.0 f ) 25 { f p r i n t f ( stderr, " \n%s Warning line %d : \

4.2. Modyfikacje platformy EASEA 32 Scaling factor must be value in [ 0, 1 ]. \ Value 1 inserted.\n. ", sez_file_name, EASEALexer. yylineno ) ; nwarnings++; 30 fscaling_factor = 1.0 f ; } }... CROSSOVER_TYPE IDENTIFIER2 35 { int range = s i z e o f ( crossover_type ) / s i z e o f ( char ) ; bool ok = 0; for ( int i =0; i <range ; ++ i ) { 40 i f (! mystricmp ( $2 >sname, crossover_type [ i ] ) ) { ncrossover_type= i ; ok = 1; break ; 45 } } i f (! ok ) { 50 f p r i n t f ( stderr, " \n%s Warning line %d : \ Incorrect crossover type.\n \ Possible values : ", sez_file_name, EASEALexer. yylineno ) ; for ( int i =0; i <range ; ++ i ) { 55 f p r i n t f ( stderr, " %s ", crossover_type [ i ] ) ; } fputs ( ". \ n", stderr ) ; f p r i n t f ( stderr, " Default value %s inserted.\n. ", crossover_type [CROSSOVER_DEFAULT ] ) ; 60 nwarnings++; ncrossover_type=crossover_default; } } MS: over Wydruk 4.10. Reguły analizatora składniowego dotyczace odczytu parametrów z pliku konfiguracyjnego. Odczytana wartość parametru zostaje wstawiana przez generator w wynikowym kodzie w miejsce symbolu. Rozszerzenie zostało omówione wyłacznie na podstawie utworzonych symboli SCALING_FACTOR oraz CROSSOVER_TYPE, które odpowiadaja kolejno parametrowi F oraz rodzajowi krzyżowania w algorytmie ewolucji różnicowej. Pokazuja one, jak wyglada pobieranie z pliku konfiguracyjnego wartości liczbowych i łańcuchów znakowych oraz wstawienie ich do kodu wynikowego. Pozostałe symbole (VECTOR_DIFFERENCES_NUM, CROSSOVER_CONSTANT, CONSTRA- INT_TYPE) zostały obsłużone w sposób analogiczny, ale dla przejrzystości rozwiazania nie zostały zamieszczone w pracy.

4.2. Modyfikacje platformy EASEA 33 Odpowiednie definicje oraz zmienne służace do obsługi przez parser łańcuchów znakowych, odpowiadajacych wyborowi rodzaju krzyżowania i metody naprawy punktów w algorytmie ewolucji różnicowej, zostały zamieszczone w pliku EaseaDEExtension.h. Sa one pokazane na wydruku 4.11. 1 //MS: f i l e with DE parameters and default values #define CROSSOVER_BINOMIAL 0 #define CROSSOVER_EXPOTENTIAL 1 #define CROSSOVER_DEFAULT CROSSOVER_BINOMIAL 5 #define CONSTRAINT_NONE 0 #define CONSTRAINT_CONSERVATISM 1 #define CONSTRAINT_REINITIALIZATION 2 #define CONSTRAINT_REFLECTION 3 #define CONSTRAINT_PROJECTION 4 10 #define CONSTRAINT_WRAPPING 5 #define CONSTRAINT_RESAMPLING 6 #define CONSTRAINT_MIDPOINT_ORIENTED 7 #define CONSTRAINT_DEFAULT CONSTRAINT_NONE static char crossover_type [ ] = { " binomial ", " exponential ", } ; 15 static char constraint_type [ ] = { "none", " conservatism ", " r e i n i t i a l i z a t i o n ", " r e f l e c t i o n ", " projection ", " wrapping ", " resampling ", " midpoint_oriented ", } ; 20 //MS: over Wydruk 4.11. Definicje oraz zmienne wykorzystywane przez parser do obsługi łańcuchów znakowych. Symbole zastępowane kodem źródłowym Na potrzeby operacji przeprowadzanych w algorytmie ewolucji różnicowej (dodawanie i odejmowanie osobników oraz skalowanie osobnika przez współczynnik F ), dodano do generatora kodu obsługę symboli INSERT_ADDITION, IN- SERT_SUBTRACTION i INSERT_MULTIPLICATION. Umożliwia to zdefiniowanie przez użytkownika w pliku konfiguracyjnym, jak wyglada operacja sumowania, odejmowania osobników oraz mnożenia osobnika przez stała. Dodana została również obsługa symbolu INSERT_BOUNDARIES_INITIALISER, co pozwala na zdefiniowanie sposobu inicjalizacji ograniczeń kostkowych w pliku konfiguracyjnym. Sposób ich dodania opierał się na wykorzystaniu istniejacej już w regułach leksera procedury COPY_USER_FUNCTION. Na wydruku 4.12 pokazany jest sposób dodania symbolu INSERT_ADDITION, odpowiadajacego operacji sumowania osobników. 4.2.2. Kompilacja platformy EASEA z rozszerzeniami w generatorze kodu Jedna z największych trudności w rozszerzeniu generatora kodu był fakt, że zbudowany został on z wykorzystaniem oprogramowania AYACC i ALex. Jedyne

4.3. Sposób użycia algorytmu DE w środowisku EASEA 34 1 //MS: necessary part to copy addition into f i l e, <TEMPLATE_ANALYSIS>"\\INSERT_ADDITION" { yreset ( ) ; 5 yin = fpgenomefile ; linecounter =1; BEGIN COPY_ADDITION; } //MS: defining what should be put in the f i l e 10 <COPY_ADDITION>"\\GenomeClass : : addition " [ \t\n ] " : " { bwithinmutator=1; i f ( bline_num_ez_file ) f p r i n t f ( fpoutputfile,"# line %d \"%s. ez\"\n", 15 linecounter, sraw_project_name) ; BEGIN COPY_USER_FUNCTION; return USER_ADDITION; } //MS: over Wydruk 4.12. Realizacja dodania symbolu odpowiadajacego za procedurę sumowania osobników. dostępne w pozycji [18] narzędzia były w wersji wyższej od tych, jakimi zbudowane zostały parser i lekser w platformie EASEA, przez co niemożliwym było ich ponowne wygenerowanie. Wymagało to dokonania w pliku reguł leksera EaseaLex.l oraz pliku reguł analizatora składni EaseaParse.y zmian, pokazanych na wydruku 4.13. Deklarowały one zmienne i makrodefinicje potrzebne w nowej wersji oprogramowania, które niedostępne były w starej. Konieczne było także dodanie do struktury oprogramowania AYACC i ALex plików yyclex.h oraz yycpars.h, które rozwiazuj a niespełnione zależności projektowe. Kolejna niedogodnościa był brak dokumentacji oprogramowania AYACC i ALex oraz fakt, że jest ono dostępne wyłacznie na platformy z systemem operacyjnym Windows, a składnia tworzenia ich reguł jest niekompatybilna z dostępnymi zarówno w systemie Linux jak i Windows narzędziami. Wydaje się to niedopuszczalne w przypadku platformy, która ma być dostępna na oba wymienione wcześniej systemy operacyjne. Rozwiazanie problemu wymagało dogłębnej analizy plików z regułami składniowymi leksera oraz parsera, która pozwoliła na dodanie nowych funkcjonalności. 4.3. Sposób użycia algorytmu DE w środowisku EASEA Wykorzystanie algorytmu zostało umożliwione poprzez dodanie do generatora kodu obsługi z linii poleceń nowych flag -de oraz -cuda_de. Ich użycie powoduje, że wygenerowany zostanie kod na podstawie stworzonych szablonów (odpowiednio STD_DE dla pierwszej oraz CUDA_DE dla drugiej flagi). Przykładowe wywołania pokazane sa na wydruku 4.14.

4.3. Sposób użycia algorytmu DE w środowisku EASEA 35 1 #define _YL #define yyfalse 0 #define yytrue 1 #include " EaseaDEErrorRemove. h" 5 / EaseaDEErrorRemove. h Created on : Jun 3, 2012 10 Author : MS / #ifndef EASEADEERRORREMOVE_H_ #define EASEADEERRORREMOVE_H_ 15 //MS: dummy variables to remove compilation er ror s static void yytokendestbaseptr ; static int yytokendestbase_size ; static int yystack_max ; static int yyunput_max ; 20 static int yytext_max ; //MS: over #endif / EASEADEERRORREMOVE_H_ / Wydruk 4.13. Usunięcie błędów kompilacji zwiazanych z niezgodnościami wersji generatora parsera i leksera. 1 $ ( EZ_PATH)/ easea de t l v plik_konfiguracyjny. ez $ ( EZ_PATH)/ easea cuda_de t l v plik_konfiguracyjny. ez $ ( EZ_PATH) zmienna okreslajaca sciezke do platformy EASEA w 5 systemie plikow Wydruk 4.14. Przykłady użycia środowiska EASEA do generacji kodu algorytmu ewolucji różnicowej. Przy dodawaniu do środowiska EASEA możliwości obsługi algorytmu ewolucji różnicowej podjęto ponizej opisane decyzje projektowe: Rozmiar osobnika n definiuje się poprzez makrodefinicję SIZE. Wektor cech osobnika ma nazwę x, wymiar SIZE oraz jest typu float. Okazało się to rozwiazaniem akceptowalnym na potrzeby testowania algorytmu w niniejszej pracy. Umożliwienie użytkownikowi definiowania własnej postaci wektora cech o dowolnym typie wymagałoby wykorzystania bardzo skomplikowanego metaprogramowania przy implementacji operatorów krzyżowania, które i tak nie dawałoby pełnej dowolności przy nazewnictwie i typie danych. Wektor dolnej granicy ograniczeń kostkowych ma nazwę lower, natomiast górnej granicy upper. Obie tablice maja rozmiar SIZE. Możliwe do zastosowania operatory krzyżowania to: dwumianowe (binomial), wykładnicze (exponential). Wyboru jednej z opcji dokonuje się poprzez umieszczenie jej angielskiej nazwy w pliku konfiguracyjnym w odpowiadaja- cym polu.

4.3. Sposób użycia algorytmu DE w środowisku EASEA 36 Możliwe do zastosowania metody uwzględniania ograniczeń kostkowch sa opisane w rozdziale 2.5.2. Wyboru jednej z opcji dokonuje się poprzez umieszczenie jej angielskiej nazwy w pliku konfiguracyjnym w odpowiadajacym polu. W przypadku ustawienia wielkości populacji potomnej różnej od rozmiaru populacji rodziców, środowisko wykorzystuje rozmiar populacji rodziców. Środowisko wymusza ustawienie opcji Surviving parents: 0% i Surviving offspring: 100%. Jest to wynik dostosowania do istniejacego już w platformie EASEA sposobu redukcji populacji. W zwiazku z tym, dla danej modyfikacji środowiska EASEA niewykorzystywane sa opcje: Reduce offspring operator oraz Final reduce operator. Reduce parents operator, Środowisko wymusza wyłaczenie opcji przechowywania zbioru elitarnego. Powoduje to, że opcje Elitism oraz Elite sa niewykorzystywane. Opcje Mutation probability oraz Crossover probability sa nieużywane, zgodnie z definicja algorytmu ewolucji różnicowej. Przykładowy fragment pliku konfiguracyjnego, w którym ustawiane sa wartości parametrów dotyczacych algorytmu ewolucji różnicowej, których dodanie do środowiska opisane było w rozdziale 4.2.1, pokazany jest na wydruku 4.15. 1 \GenomeClass : : addition : for ( int i =0; i <SIZE ; i ++ ) { x [ i ] += right. x [ i ] ; 5 } //right j e s t nazwa osobnika, ktory j e s t dodawany \end \GenomeClass : : subtraction : 10 for ( int i =0; i <SIZE ; i ++ ) { x [ i ] = right. x [ i ] ; } //right j e s t nazwa osobnika, ktory j e s t odejmowany 15 \end \GenomeClass : : multiplication : for ( int i =0; i <SIZE ; i ++ ) { 20 x [ i ] =A; } //A j e s t wartoscia zmiennej, przez ktora mnozony j e s t wektor \end 25 \GenomeClass : : boundaries_initialiser : for ( int i =0; i <SIZE ; i ++ ) { lower [ i ] = X_MIN; upper [ i ] = X_MAX; 30 } //lower i upper to nazwy t a b l i c, w ktorych przechowywane //sa wartosci ograniczen kostkowych dla poszczegolnych //wymiarow. Maja one, podobnie jak wektory cech osobnikow, //wymiar SIZE. 35 \end

4.3. Sposób użycia algorytmu DE w środowisku EASEA 37 \Default run parameters : // Please l e t the parameters appear in this order... Scaling factor : 0.9 40 Vector differences num: 1 Crossover constant : 0.9 Crossover type : binomial Constraint type : r e f l e c t i o n \end Wydruk 4.15. Przykład ustawienia parametrów algorytmu ewolucji różnicowej.

5. Testowanie jakości wyników uzyskiwanych przez algorytm ewolucji różnicowej Aktualnie istnieje wiele zaproponowanych przez naukowców metod optymalizacji globalnej funkcji zmiennych rzeczywistych. Ocena ich faktycznej jakości wymaga precyzyjnych i jednakowych warunków, w jakich odbywa się ich badanie. Właśnie w tym celu utworzono zestawy funkcji testowych (ang. benchmarks), które pozwalaja na porównanie wyników uzyskanych przez różne metody optymalizacji. Dzięki nim możliwe jest także badanie wpływu parametrów występujacych w algorytmie na jakość otrzymywanych rozwiazań. 5.1. Zestaw funkcji testowych CEC2005 Przy wykonywaniu testów zaimplementowanego w środowisku EASEA algorytmu ewolucji różnicowej, posłużono się popularnym zbiorem funkcji testowych CEC2005. Został on przedstawiony na jednej z pierwszych konferencji, których celem było wyłonienie najlepszych algorytmów ewolucyjnych znanych w danym czasie. Jest opisany w pozycji [14]. Składa się z 25 funkcji, które sa podzielone na poniższe grupy, w zależności od swoich właściwości: Funkcje unimodalne (1-5) Funkcje bazowe multimodalne (6-12) Rozszerzone funkcje multimodalne (13-14) Funkcje złożone multimodalne (15-25) Funkcje złożone występujace w zbiorze sa utworzone z dostępnych w nim funkcji unimodalnych oraz multimodalnych (Ackley a, Rastrigina, Griewanka, Weierstrassa, Schwefela). Większość z nich jest zaprezentowana w zmodyfikowanych układach współrzędnych, które sa przesunięte, obrócone lub przeskalowane i sa dla nich zdefiniowane ograniczenia kostkowe. Dostępne sa także dwa zadania bez ograniczeń kostkowych (f7 i f25) oraz dwie funkcje niedeterministyczne (f4 i f17), które zawieraja szum. Możliwe jest wykonywanie testów dla różnej wymiarowości funkcji (2, 10, 30 oraz 50). W zwiazku z tymi cechami, benchmark CEC2005 jest dobrym standardem testowym i punktem odniesienia dla metod optymalizacji globalnej, pomimo faktu, że w momencie tworzenia niniejszej pracy ma już 7 lat.

5.2. Modyfikacje benchmarku CEC2005 39 5.2. Modyfikacje benchmarku CEC2005 Założeniem pracy było wykonanie testów przy użyciu środowiska EASEA skonfigurowanego na obliczanie wartości funkcji celu osobników z wykorzystaniem karty graficznej. W zwiazku z faktem, że implementacja funkcji w zestawie CEC2005 opiera się wyłacznie na zmiennych globalnych, możliwe jest ich obliczanie wyłacznie na jednostce centralnej w aplikacji jednowatkowej. Uruchomienie w wielowatkowym środowisku karty graficznej powodowało ryzyko hazardu, w zwiazku z tym wymagane było zmodyfikowanie kodu źródłowego. W pierwszej kolejności należało zapewnić, aby każdy watek obliczajacy wartość funkcji celu otrzymał swój własny zestaw zmiennych, na których wykonuje obliczenia. Wprowadzone zostały do kodu źródłowego dwie klasy, które pokazane sa na wydruku 5.1. 1 class CParams { public : float C; 5 float norm_x [ nreal ] ; float norm_f [ nfunc ] ; float trans_x [ nreal ] ; float basic_f [ nfunc ] ; float temp_x1 [ nreal ] ; 10 float temp_x2 [ nreal ] ; float temp_x3 [ nreal ] ; float temp_x4 [ nreal ] ; float weight [ nfunc ] ; } ; 15 class GlobalParams { public : float global_bias ; 20 float sigma [ nfunc ] ; float lambda [ nfunc ] ; float bias [ nfunc ] ; float o [ nfunc ] [ nreal ] ; float l [ nfunc ] [ nreal ] [ nreal ] ; 25 float g [ nreal ] [ nreal ] ; #i f defined f5 float A[ nreal ] [ nreal ] ; float B[ nreal ] ; #e l i f defined f12 30 float A[ nreal ] [ nreal ] ; float B[ nreal ] [ nreal ] ; float alpha [ nreal ] ; #endif } ; Wydruk 5.1. Klasy agregujace zmienne globalne w CEC2005. Klasa CParams agreguje zmienne, które każdy z watków karty graficznej wykorzystuje lokalnie, zaś klasa GlobalParams zawiera zmienne globalne w kontekście całej funkcji. Sposób inicjalizacji zmiennych przebiega podobnie do oryginalnych funkcji CEC2005, z wyjatkiem statycznej alokacji opisanych klas

5.2. Modyfikacje benchmarku CEC2005 40 agregujacych. Po tej procedurze dodano kopiowanie danych do pamięci karty graficznej. Inicjalizacja i transfer danych pokazane zostały na wydruku 5.2. 1 CParams cec05pars ; device CParams d_cec05pars ; GlobalParams globalparams ; device GlobalParams d_globalparams ; 5 allocate_memory ( cec05pars, globalparams ) ; / Routine the i n i t a l i z e global variables / i n i t i a l i z e ( globalparams ) ; 10 / For t e s t problems 15 to 25, we need to calculate a normalizing quantity / # i f defined f15 defined f16 defined f17 defined f18 defined f19 defined f20 defined f21 defined f22 defined f23 defined f24 defined f25 15 calc_benchmark_norm ( cec05pars, globalparams ) ; #endif cudamemcpytosymbol ( d_cec05pars, &cec05pars, sizeof ( cec05pars ), 0, cudamemcpyhosttodevice ) ; 20 cudamemcpytosymbol ( d_globalparams,&globalparams, sizeof ( globalparams ), 0, cudamemcpyhosttodevice ) ; Wydruk 5.2. Inicjalizacja klas agregujacych dane oraz ich kopiowanie do pamięci karty graficznej. Wywołanie funkcji celu z benchmarku CEC2005 wymagało zmodyfikowania występujacych w ich definicji odwołań do wszystkich zmiennych globalnych tak, aby dotyczyły one zmiennych zawartych w obiektach klas podawanych jako parametry funkcji. Pozwoliło to na łatwa zmianę funkcji w ten sposób, żeby obliczenia dotyczyły lokalnego zestawu zmiennych i było możliwe równoległe ich wykonywanie. Pokazane jest to na wydruku 5.3. Jako, że klasa GlobalParams jest wyłacznie odczytywana przez wszystkie watki, możliwe było jej podanie przez referencję. 1 device host inline float cec05 ( float x, CParams d_pars, GlobalParams &d_global_pars ) { 5 return ( calc_benchmark_func ( x, d_pars, d_global_pars ) ) ; } device host inline float calc_benchmark_func ( float x, CParams &d_pars, 10 GlobalParams &d_global_pars ) { //zmiana naglowkow funkcji benchmarku } Wydruk 5.3. Wywołanie funkcji z benchmarku CEC2005. Modyfikacje wprowadzone do funkcji inicjalizujacych oraz funkcji składowych zostały wykonane w ten sposób, aby ten sam kod źródłowy mógł być skompilowany do wykonywania nie tylko na karcie graficznej, ale i jednostce centralnej. Wszystkie wymagane funkcje sa deklarowane z kwalifikatorami device

5.3. Ustawienia algorytmu w przeprowadzonych testach 41 host, stad przy kompilacji tworzone sa wersje tej funkcji dla obu jednostek kompilacji. Wybór funkcji do wywołania odbędzie się wtedy poprzez to, czy jako parametry funkcji zostana podane obiekty klas będace w pamięci urzadzenia nadrzędnego, czy podrzędnego. Przykład pokazany jest na wydruku 5.4. 1 float x [ SIZE ] ; CParams cec05pars ; device CParams d_cec05pars ; GlobalParams globalparams ; 5 device GlobalParams d_globalparams ; / Wywolanie f u n k c j i dla urzadzenia nadrzednego / cec05 ( x, cec05pars, globalparams ) ; 10 / Wywolanie f u n k c j i dla urzadzenia podrzednego / cec05 ( x, d_cec05pars, d_globalparams ) ; Wydruk 5.4. Wywołanie funkcji z benchmarku CEC2005 dla urzadzenia nadrzędnego lub podrzędnego. 5.3. Ustawienia algorytmu w przeprowadzonych testach Uzyskane przez algorytm ewolucji różnicowej wyniki sa zależne od złożoności rozwiazywanego zagadnienia, czyli od wymiarowości zadania. Istotny wpływ na jakość generowanych przez algorytm ewolucji różnicowej rozwiazań ma także odpowiedni dobór parametrów. We wszystkich przeprowadzonych testach proces inicjalizacji miał identyczna postać i odbywał się poprzez losowanie osobników z rozkładu jednostajnego w zbiorze dopuszczalnym (dla funkcji bez ograniczeń kostkowych inicjalizacja odbywała się w sposób określony w pozycji [14]). Wykorzystywana była wersja algorytmu DE/rand/1/bin, z modyfikacja polegajac a na dodaniu do osobnika zmutowanego wektora, którego współrzędne losowane sa z rozkładu jednostajnego w hipersześcianie [ (u l) ]. Wybrane zostały parametry zaproponowane 100, (u l) 100 w pozycji [15], które sa standardowymi dla algorytmu DE/rand/1/bin: F = 0.9 CR = 0.9 µ {100, 1000, 10000} n = 50 liczba generacji - T = 10000 metoda naprawy - odbijanie Ze względu na probabilistyczna naturę ewolucji różnicowej, nie można polegać na pojedynczym przebiegu algorytmu. Wykonane zostało zatem po K = 25 przebiegów dla każdej instancji problemu (konfiguracji podanych powyżej parametrów). Wartość ta jest zasugerowana w pozycji [14] i wydaje się być dobrym

5.4. Wyniki 42 kompromisem pomiędzy czasem wykonania testów, a wiarygodnościa otrzymanych wyników. W analizie uzyskanych rezultatów posłużono się uśrednionym najlepszym dopasowaniem (ang. mean best quality - MBQ). W każdym przebiegu algorytmu (k = 1, 2,.., K) po każdej generacji (t = 1, 2,..., T ) raportowano wartość najlepszego dopasowania spośród całej populacji i jej środka geometrycznego (wektor b k ): b k = [b 1 k, b2 k,..., bt k ] (5.1) Z uzyskanych wartości obliczono średnie arytmetyczne z najlepszych dopasowań po każdej generacji, liczone po wszystkich 25 przebiegach (MBQ t ). Następnie wyznaczono różnice między uzyskanymi wartościami średnimi, a wartościa minimalna funkcji celu, które w dalszej części pracy nazywane sa błędami: E = [e 1, e 2,..., e T ] e t = K b t k K f(x opt ) = MBQ t f(x opt ) k=1 Obliczone błędy sa jednym ze wskaźników efektywności algorytmu. (5.2) Określaja, jak dobre rozwiazanie jest możliwe do znalezienia w określonych limitach (np. czasie działania lub liczbie generacji algorytmu). 5.4. Wyniki W procesie testowania wykorzystane zostały funkcje 1-15 z benchmarku CEC2005. Uzyskane wyniki błędu w zależności od liczby generacji przedstawiono na wykresach. Dla przejrzystości analizy, w niniejszej pracy zamieszczone i omówione zostały rezultaty dla wybranych funkcji: f1, f2, f9, f11, f12 oraz f15. Wyniki uzyskane dla wszystkich wykorzystanych funkcji (1-15) z zestawu CEC2005 zamieszczone sa na płycie dołaczonej do pracy. W przypadku zastosowania funkcji f1 jako funkcji celu, wykres na rysunku 5.1 pozwala zauważyć, że w pracy algorytmu dało się wyróżnić jedynie fazę eksploatacji. Na wykresie odpowiadajacym funkcji f2 (widocznym na rysunku 5.2), w poczatkowej fazie pracy algorytmu widoczne sa różnice w wartościach błędu w zależności od rozmiaru populacji. Wynika to z różnorodności dynamiki funkcji w różnych kierunkach, która jest widoczna na jej wykresie (rysunek B.2). W działaniu algorytmu ewolucji różnicowej dla funkcji celu f2 da się wyróżnić poczat- kowa fazę eksploracji, po której następuje faza eksploatacji. W sytuacji zastosowania funkcji f11 jako funkcji celu, algorytm ewolucji różnicowej jest wyłacznie w fazie eksploracji, nie następuje zaś przejście do fazy eksploatacji. Zachowanie to obrazuje rysunek 5.3. Algorytm wykazuje działanie

5.4. Wyniki 43 10 6 Funkcja f1 10 5 rozmiar populacji 100 rozmiar populacji 1000 rozmiar populacji 10000 10 4 błąd 10 3 10 2 10 1 10 0 10 1 10 2 10 3 10 4 generacja Rysunek 5.1. Bład w zależności od liczby generacji dla funkcji nr 1 z zestawu funkcji testowych CEC2005. 10 6 Funkcja f2 rozmiar populacji 100 rozmiar populacji 1000 rozmiar populacji 10000 błąd 10 5 10 4 10 0 10 1 10 2 10 3 10 4 generacja Rysunek 5.2. Bł ad w zależności od liczby generacji dla funkcji nr 2 z zestawu funkcji testowych CEC2005.

5.4. Wyniki 44 Funkcja f11 10 1.91 rozmiar populacji 100 rozmiar populacji 1000 rozmiar populacji 10000 10 1.89 błąd 10 1.87 10 1.85 10 1.83 10 0 10 1 10 2 10 3 10 4 generacja Rysunek 5.3. Bład w zależności od liczby generacji dla funkcji nr 11 z zestawu funkcji testowych CEC2005. skrajnie różne niż w przypadku funkcji f1, gdzie wyszczególnić można jedynie fazę eksploatacji. Wykres błędów uzyskanych przy użyciu funkcji f12 jako funkcji celu pokazany jest na rysunku 5.4. W działaniu algorytmu ewolucji różnicowej można wyróżnić poczatkow a fazę eksploracji. Dla populacji o rozmiarze 100 widoczne jest późniejsze przełaczenie algorytmu na eksploatację. Wykres 5.5, obrazujacy działanie algorytmu dla funkcji f15, pokazuje, że poczatkowa faza eksploracji spowodowała uchwycenie ogólnej tendencji przebiegu funkcji (widocznej na rysunku B.6). Osiagnięty wynik błędu pozwala wywnioskować, że populacja znalazła się w otoczeniu optimum lokalnego, które zostało znalezione w późniejszej fazie eksploatacji. Trudne do zinterpretowania sa wyniki uzyskane przy zastosowaniu funkcji f9 jako funkcji celu (widoczne na rysunku 5.6). Zbadanie dokładnego zachowania algorytmu w tej sytuacji wymagałoby dogłębnej analizy, co przekracza zakres niniejszej pracy. Interesujacym zjawiskiem jest także osiaganie najlepszych wyników przez najmniejsza populację w przypadku funkcji f1 oraz f12. Dokładne ustalenie warunków, w jakich zastosowanie mniejszych populacji daje lepsze jakościowo rozwiazania, wykracza jednak poza zakres niniejszej pracy.

5.4. Wyniki 45 10 7 Funkcja f12 błąd 10 6 rozmiar populacji 100 rozmiar populacji 1000 rozmiar populacji 10000 10 5 10 0 10 1 10 2 10 3 10 4 generacja Rysunek 5.4. Bład w zależności od liczby generacji dla funkcji nr 12 z zestawu funkcji testowych CEC2005. 1400 1300 1200 1100 Funkcja f15 rozmiar populacji 100 rozmiar populacji 1000 rozmiar populacji 10000 1000 błąd 900 800 700 600 500 400 10 0 10 1 10 2 10 3 10 4 generacja Rysunek 5.5. Bł ad w zależności od liczby generacji dla funkcji nr 15 z zestawu funkcji testowych CEC2005.

5.4. Wyniki 46 Funkcja f9 10 2.9 rozmiar populacji 100 rozmiar populacji 1000 rozmiar populacji 10000 10 2.8 błąd 10 2.7 10 2.6 10 0 10 1 10 2 10 3 10 4 generacja Rysunek 5.6. Bład w zależności od liczby generacji dla funkcji nr 9 z zestawu funkcji testowych CEC2005. Przedstawione powyżej wyniki pokazuja, że przy wykorzystaniu jednakowych parametrów, algorytm ewolucji różnicowej wykazuje różnorodne zachowanie w zależności od rozpatrywanego problemu. Potwierdza to spostrzeżenie, że uzyskanie dobrych jakościowo rezultatów wymaga osobnego dostrojenia parametrów algorytmu ewolucji różnicowej do rozwiazywanych zadań. Wyniki wykonanych testów pozwalaja zauważyć, że algorytm ewolucji różnicowej w fazie eksploracji pracuje dobrze z dużymi populacjami, natomiast w fazie eksploatacji z małymi. Wydaje się zatem, że dalsze prace nad zagadnieniem ewolucji różnicowej mogłyby polegać na analizie wpływu zmiany rozmiaru populacji w zależności od fazy algorytmu na jakość generowanych rozwiazań.

6. Własna implementacja algorytmu DE z wykorzystaniem technologii CUDA Środowisko EASEA opisane w rozdziale 4 oferuje wiele funkcji i ułatwień dla osób chcacych wykorzystać algorytmy ewolucyjne. Dla przyspieszenia obliczeń korzysta z technologii CUDA, umożliwiajacej działanie programu na wielordzeniowych procesorach graficznych. Wyjatkow a cecha EASEA jest jego wszechstronność polegajaca na umożliwieniu osobom o podstawowej wiedzy na temat programowania zdefiniowania własnej wersji algorytmu ewolucyjnego. Zapewnienie takiej elastyczności platformy spowodowało jednak wiele ustępstw, które negatywnie wpłynęły na faktyczna wydajność działania generowanej aplikacji. W celu udowodnienia sformułowanego powyżej spostrzeżenia, zaimplementowany został dedykowany dla algorytmu DE program w języku C++ oraz CUDA C, wykorzystujacy w znacznie większym stopniu potencjał kart graficznych do przyspieszania obliczeń. W niniejszym rozdziale przedstawione zostanie wykorzystane do utworzenia wydajnej implementacji środowisko pracy, a następnie zaprezentowany będzie utworzony program. 6.1. Środowisko pracy i karta graficzna NVIDIA GTX 680 W celu pokazania możliwych do uzyskania przyspieszeń wykorzystany został potencjał karty NVIDIA GTX 680. Jej budowa opiera się o najnowsza architekturę o nazwie Kepler. Zastosowano w niej nowy typ multiprocesor ów nazwanych SMX, które w swojej budowie wewnętrznej zawieraja aż 192 rdzenie. Budowa jednostek wykonawczych nowych procesorów graficznych została odpowiednio zoptymalizowana. Usunięto z niej niektóre bloki sprzętowe odpowiedzialne za obliczanie opóźnienia wykonania instrukcji (które jest deterministyczne i ustalane przez kompilator), a także wykonano relokację wewnętrznych zatrzasków oraz rejestrów, co pozwoliło na przyspieszenie przetwarzania. W skład karty GTX 680 wchodzi osiem SMX, które składaja się na 1536-rdzeniowa platformę obliczeniowa, w której, dla podstawowej wersji, rdzenie taktowane sa zegarem o częstotliwości 1066 MHz. Jeżeli chodzi o użycie interfejsu programistycznego CUDA z punktu widzenia utworzenia implementacji algorytmu DE, środowisko CUDA nie udostępnia wielu nowych funkcjonalności w architekturze karty GTX 680 (Kepler GK104) w porównaniu do poprzednich. Za bardzo istotna zaletę można uznać znaczna

6.2. Zrównoleglenie algorytmu 48 liczbę szybkich rdzeni procesorów graficznych oraz ulepszenia zwiazane z używaniem instrukcji atomowych. W danej architekturze zwiększono ich wydajność, dzięki czemu w jednym takcie zegara moga wykonać się nawet 64 instrukcje atomowe. 6.2. Zrównoleglenie algorytmu W środowisku EASEA jedyne zlecenia wysyłane do wykonania na karcie graficznej dotycza obliczania wartości funkcji celu osobników. Ze względu na to, że cały algorytm cechuje możliwość wysokiego zrównoleglenia wykonywanych operacji, postanowiono wykonać wszystkie obliczenia zwiazane z ewolucja różnicowa na karcie graficznej, co przynosi znacznie większy zysk czasowy. Algorytm podzielony został na funkcje, w których operacje wykonywane sa równolegle: utworzenie osobnika potomnego dla każdego osobnika populacji (mutacja, krzyżowanie i naprawa punktów będacych poza granicami) obliczanie funkcji celu redukcja populacji z wykorzystaniem operatora sukcesji elitarnej znalezienie najlepszego osobnika obliczanie środka geometrycznego populacji obliczanie przystosowania środka geometrycznego populacji Po wykonaniu każdego z kroków konieczna jest synchronizacja w celu odczytania wyników potrzebnych do wykonania kolejnego z nich. Każdemu z kroków odpowiada oddzielne jadro (rozdział 3.1.1), wewnatrz którego operacje wykonywane sa równolegle. Wszystkie opisane powyżej kroki wykonuja się na karcie graficznej. W zwiazku z tym faktem, wszystkie dane dotyczace bieżacej populacji oraz populacji potomków przechowywane sa wyłacznie w pamięci urzadzenia podrzędnego. Na koniec każdej generacji jedynie wartość funkcji celu najlepszego osobnika i środka geometrycznego przepisywane sa do pamięci urzadzenia nadrzędnego, co prowadzi do znacznego ograniczenia transferów pomiędzy pamięcia karty graficznej a pamięcia urzadzenia nadrzędnego i pozwala na istotny wzrost wydajności programu. Następnie, zgodnie z idea programowania heterogenicznego opisana w rozdziale 3.1.1, sterowanie zostaje przekazane do urzadzenia nadrzędnego, gdzie odbywa się zapisywanie czastkowych wyników. 6.3. Reprezentacja i przechowywanie danych W środowisku EASEA podejście utrzymywania populacji jako tablicy obiektów klasy reprezentujacej osobnika jest intuicyjne i pozwala na proste zrozumienie z pozycji użytkownika. Specyfika obliczeń na karcie implikuje jednak nieco

6.4. Losowanie danych 49 Rysunek 6.1. Reprezentacja populacji przechowywanej w pamięci karty graficznej. odmienna reprezentację danych. Każde pokolenie widziane jest jako macierz, w której wiersze odpowiadaja kolejnym osobnikom, a kolumny - poszczególnym wartościom w wektorach liczb zmiennoprzecinkowych charakteryzujacych osobniki. Reprezentację pokazuje rysunek 6.1. Wartości funkcji celu osobników przechowywane sa natomiast w osobnym wektorze liczb. 6.4. Losowanie danych Losowanie danych w algorytmie zrealizowane jest za pomoca biblioteki CU- RAND (pozycja [12]) i dostępnego w niej domyślnego generatora liczb pseudolosowych XORWOW. Aplikacja korzysta z interfejsu programowania od strony urza- dzenia podrzędnego - inicjalizacja i losowanie wywoływane jest w jadrze. Zgodnie z regułami zamieszczonymi w pozycji [12], najlepsza praktyka jest przechowywanie przez każdy watek swojego lokalnego stanu generatora, które razem składaja się na stan globalny. Na poczatku programu następuje zatem przydzielenie pamięci dla tablicy globalnego stanu generatora. Inicjalizacja stanów lokalnych powoduje utworzenie dla każdego watku unikalnej sekwencji liczb pseudolosowych. Dla zapewnienia braku korelacji pomiędzy sekwencjami utworzonymi dla różnych watków, przy inicjalizacji wykorzystywane jest to samo ziarno losowe oraz unikalne numery sekwencji generatora (zgodnie z zaleceniami zawartymi w pozycji [12]). 6.5. Szczegóły implementacji 6.5.1. Wykorzystanie pamięci niepodlegajacej wymianie W celu zwiększenia wydajności programu, do alokacji danych wymienianych pomiędzy pamięcia urzadzenia nadrzędnego a pamięcia urzadzenia podrzędnego w każdej iteracji algorytmu, wykorzystywana jest pamięć niepodlegajaca wymianie (ang. page host locked memory), która została opisana w rozdziale 3.1.3.

6.5. Szczegóły implementacji 50 Obliczanie populacji przez bloki wątków blok 0 blok 1 blok 2... osobnik 1 osobnik 2 osobnik 3 osobnik 4 osobnik 5 osobnik 6 osobnik 7 osobnik 8 Rysunek 6.2. Schemat tworzenia populacji potomnej dla bloku o rozmiarze 3 3. Ilość danych odczytywanych z poziomu urzadzenia nadrzędnego jest niewielka - jedna wartość funkcji celu najlepszego osobnika i jedna wartość funkcji celu środka geometrycznego populacji, stad działanie aplikacji nie spowoduje spadku wydajności całego systemu. W programie łatwo można także zaalokować taka pamięć dla całego najlepszego osobnika oraz środka geometrycznego. 6.5.2. Tworzenie osobników potomnych Podział na bloki Przy wywołaniu jadra zdefiniowany jest rozmiar bloku watków X Y. Każdemu watkowi przypisany jest indeks i osobnika, który jest wyznaczany na podstawie umiejscowienia bloku w siatce i współrzędnej y watku w bloku. Każdy watek w bloku jest odpowiedzialny za obliczenie x-tej współrzędnej i-tego osobnika w populacji (x {1...X}, y {1...Y }, i {1...µ}). Po obliczeniu elementu, watki obliczajace współrzędne i-tego osobnika wyznaczaja kolejna współrzędna na pozycji x + X, aż do obliczenia wszystkich współrzędnych odpowiadajacego im osobnika. Oznacza to, że watek o indeksach (x, y) w bloku, oblicza współrzędne osobnika o indeksach: (x, i), (x + X, i), (x + 2X, i),..., (n X + x, i), gdzie n jest rozmiarem osobnika. Liczba bloków zależy od wielkości populacji. Przy rozmiarze µ powołanych zostanie µ/y bloków. Schemat obliczania populacji potomnej dla bloku watków 3 3 obrazuje rysunek 6.2. Wykonywane operacje Do zadań każdego watku należy obliczenie odpowiednich współrzędnych osobnika potomnego. Zgodnie z algorytmem DE, na poczatku określane sa indeksy osobników bioracych udział w mutacji. Sa one losowane przez te watki, które w dwuwymiarowym bloku maja pozycję x równa 0. Następnie następuje synchronizacja, aby wszystkie watki odczytały właściwe wylosowane wartości.

6.5. Szczegóły implementacji 51 Indeksy osobników zapisywane sa w pamięci współdzielonej, do której dostęp jest szybszy niż do pamięci globalnej (rozdział 3.1.1). Następnie watki obliczaja przypisane im współrzędne osobnika zmutowanego. W zwiazku z faktem, że każda współrzędna osobników potomnych zapisywana jest tylko jeden raz, wyniki zostaja od razu umieszczone w pamięci globalnej. Osobniki populacji rodziców czytane sa wiele razy, jednak liczba odczytów zależy od wylosowanych indeksów. Nie da się zatem przewidzieć, ile razy jeden blok odczyta danego osobnika, ani jakie osobniki zostana odczytane. Uniemożliwia to użycie pamięci współdzielonej do redukcji odczytów osobników rodzicielskich z pamięci globalnej. Po obliczeniu konkretnej współrzędnej, od razu dokonuje się dla niej krzyżowanie wymieniajace, przez co konieczne jest utworzenie w kodzie programu rozejścia. Po krzyżowaniu następuje dlatego ponowna synchronizacja watków, aby połaczyć osobne ścieżki programu. Po niej następuje sprawdzenie, czy nowa współrzędna znajduje się w dopuszczalnym zakresie, jeżeli nie - następuje naprawa zgodnie z reguła odbijania opisana w rozdziale 2.5.2. W celu zmniejszenia liczby odczytów i zapisów do pamięci globalnej, co znaczaco poprawia wydajność (opisane w rozdziale 3.1.1), obliczanie każdej współrzędnej osobników potomnych odbywa się przy pomocy zmiennej umieszczonej w pamięci lokalnej, która dopiero w ostatecznej postaci (tzn. po operacji mutacji, krzyżowania i uwzględnienia ograniczeń kostkowych) zapisywana jest do pamięci globalnej. Podobny zabieg zastosowano w kwestii przechowywania lokalnego dla każdego watku stanu generatora. Stan odczytywany jest przez watek z pamięci globalnej na poczatku działania, w trakcie programu modyfikowana jest jego lokalna kopia, a dopiero na końcu wszystkich operacji zapisuje się jego nowa wartość do pamięci globalnej. 6.5.3. Obliczanie funkcji celu Efektywne obliczanie funkcji celu z wykorzystaniem technologii CUDA wymaga przemyślanej jej implementacji, która w znacznej mierze zależna jest od postaci funkcji. Do celów testowych i porównawczych, w programie została zaimplementowana funkcja pierwsza z zestawu funkcji testowych CEC2005 (omówionego w rozdziale 5.1). Funkcja została przedstawiona w załaczniku B.2. Podział na bloki Podział na bloki w przypadku funkcji pierwszej, wyglada tak samo jak w przypadku opisanego wyżej jadra (rozdział 6.5.2). Watki maja za zadanie naliczać, zgodnie z postacia funkcji, kwadraty różnic współrzędnych oraz odpowiadajacego wektora przesunięcia, który jest określony w równaniu B.1.

6.5. Szczegóły implementacji 52 Wykonywane operacje Dla każdego watku zaalokowana jest jedna komórka pamięci współdzielonej, w której akumulowane sa wyniki czastkowe naliczane przez poszczególny watek. Na poczatku każdy watek wpisuje do komórki pomocniczej liczbę 0. Watki przesuwaja się po wektorach osobników tak jak w przypadku jadra wyznaczajacego potomków, co zostało opisane w rozdziale 6.5.2. W jednej operacji każdy watek dodaje do odpowiadajacej mu komórki w pamięci współdzielonej kwadrat różnicy aktualnie rozpatrywanej współrzędnej i wartości z wektora przesunięcia, zgodnie z równaniem B.1. Tym sposobem, na koniec każdy blok ma utworzona pomocnicza macierz wyników czastkowych, w której trzeba zsumować kolumny, aż do otrzymania jednego, kolumnowego wektora wartości funkcji celu osobników. Sumowanie realizowane jest w standardowy dla technologii CUDA sposób. Przykład dla pojedynczego wiersza macierzy w bloku o rozmiarze 8 8 pokazany jest na rysunku 6.3. Widoczne jest, że watki o indeksie x {4, 5, 6, 7} dodaja wartości pod tymi indeksami do komórek o x {0, 1, 2, 3}. Po synchronizacji, w sposób analogiczny watki o indeksie x {2, 3} dodaja wartości pod tymi indeksami do komórek o x {0, 1}. Po kolejnej synchronizacji bloku, watek o indeksie x = 1 sumuje wartości o współrzędnych 0 oraz 1 i akumuluje go pod indeksem 0. Następnie ta wartość jest przepisana jako wynik funkcji celu rozpatrywanego osobnika. 6.5.4. Redukcja populacji poprzez sukcesję elitarna Po obliczeniu funkcji celu wszystkich osobników można przystapić do redukcji populacji poprzez sukcesję elitarna pomiędzy rodzicem, a jego potomkiem. Podział na bloki watków jest identyczny z poprzednio opisanymi jadrami i został omówiony w rozdziale 6.5.2. Każdy watek w bloku ma zadanie przepisać współrzędne lepszego osobnika do tablicy z populacja rodziców. Potrzebne jest tutaj sprawdzenie, czy taki zapis jest konieczny, tzn. czy osobnik potomny jest lepiej przystosowany. Sprawdzenia dokonuja tylko watki, których współrzędna x w bloku wynosi 0. Jeżeli wartość funkcji celu potomka jest gorsza, watki o tym samym indeksie y (odpowiadajace jednemu osobnikowi) w bloku kończa działanie. W przeciwnym wypadku, watki o współrzędnej x = 0 przepisuja wartość funkcji celu potomka dla danego im indeksu i osobnika do tablicy wartości przystosowania populacji rodziców. Potem następuje synchronizacja dla połaczenia watków, które podażyły różnymi ścieżkami programu. Watki, które sa odpowiedzialne za przepisanie współrzędnych populacji potomnej do populacji rodziców kontynuuja działanie. Przykład pracy watków pokazany jest na rysunku 6.4. Pokazuje jedna iterację przepisywania wartości przy sytuacji, gdy osobnik potomny

6.5. Szczegóły implementacji 53 + + + + Osobnik a b c d e f g h x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + + synchronizacja a+e b+f c+g d+h e f g h + synchronizacja a+e + c+g b+f + d+h c+g d+h e f g h synchronizacja b+f + d+h c+g d+h e f g h a + e + c + g + b + f+ d + h Rysunek 6.3. Sumowanie kolumn macierzy w pamięci współdzielonej pokazane na przykładzie jednego wiersza.

6.5. Szczegóły implementacji 54 Sukcesja elitarna realizowana przez bloki wątków blok 0 blok 1 blok 2 populacja dzieci 6 8 5 3 2 8 6 5... populacja rodziców 7 3 7 2 1 9 7 6 osobnik 1 osobnik 2 osobnik 3 osobnik 4 osobnik 5 osobnik 6 osobnik 7 osobnik 8 Rysunek 6.4. Realizacja sukcesji elitarnej przy bloku watków o rozmiarze 3 3. Wybranie najlepszej wartości funkcji celu jednowymiarowy blok wątków osobnik 1 osobnik 2 osobnik 3 osobnik 4 osobnik 5... osobnik 6 osobnik 7 osobnik 8 Rysunek 6.5. Przesuwanie się jednowymiarowego bloku watków przy znajdowaniu najlepszej wartości funkcji celu. jest lepiej przystosowany. Po wykonaniu działań watki przesuwaja się w prawo po macierzy populacji, aż do przepisania całego osobnika. 6.5.5. Znalezienie najlepszej wartości funkcji celu Zadanie znalezienia najlepszej wartości funkcji celu sprowadza się do przeszukania wektora zawierajacego wartości funkcji celu osobników. Podział zadań między watki W jadrze znajdujacym najlepszego osobnika watki operuja na wektorze liczb, które sa wartościami funkcji celu. Wszystkie watki musza wybrać jedna najlepsza wartość. Aby uniknać problemów synchronizacyjnych między blokami, przy wywołaniu jadra powołany jest tylko jednowymiarowy blok watków. Pracę watków obrazuje rysunek 6.5.

6.5. Szczegóły implementacji 55 < < < < 2 1 7 3 5 9-4 1 Wektor warto funkcji celu osobników < < synchronizacja 2 1-4 1 5 9-4 1 < synchronizacja -4 1-4 1 5 9-4 1 synchronizacja -4 1-4 1 5 9-4 1 Rysunek 6.6. Znajdowanie najlepszej wartości funkcji celu. Zadania watków Watki przesuwaja się wzdłuż wektora na tej samej zasadzie, jak opisana w rozdziale 6.5.2, tylko w jednym wymiarze y. Zapisuja przy tym do odpowiadajacego im pola pomocniczej tablicy umieszczonej w pamięci współdzielonej lepsze przystosowanie z każdego dokonanego porównania wartości funkcji celu. Na koniec tego działania otrzymujemy pomocniczy wektor, z którego należy wybrać już tylko jedna wartość. rysunku 6.6. Przykład dla bloku o 8 watkach pokazany został na Widoczne jest, że watki o indeksie y {4, 5, 6, 7} porównuja wartości pod tymi indeksami z zapisanymi w komórkach o y {0, 1, 2, 3}. Po synchronizacji, w sposób analogiczny watki o indeksie y {2, 3} porównuja wartości funkcji celu pod tymi indeksami z komórkami o y {0, 1}. Po kolejnej synchronizacji bloku, watek o indeksie y = 1 wybiera wartość spod współrzędnej 0 lub 1 i zapisuje go do zmiennej globalnej określajacej wartość najlepszego przystosowania. Operacje te można było wykonać na wielu blokach, jednak trudnym zadaniem jest tutaj zrealizowanie synchronizacji międzyblokowej. Niedostępna jest również funkcja atomowego wybrania maksymalnej z liczb zmiennoprzecinkowych. Ja- dro zajmuje jednak na tyle mało czasu w porównaniu do czasu wykonania całego programu, że zaprezentowana implementacja jest akceptowalna na potrzeby całego rozwiazania.

6.5. Szczegóły implementacji 56 Obliczanie punktu środkowego przez bloki wątków bloki (0,0), (0,1), (0,2) bloki (1,0), (1,1), (1,2) bloki (2,0), (2,1), (2,2) osobnik 1 osobnik 2 osobnik 3 osobnik 4 osobnik 5 osobnik 6 osobnik 7 osobnik 8... Rysunek 6.7. Podział macierzy populacji w dwuwymiarowa siatkę. 6.5.6. Obliczenie środka geometrycznego populacji Obliczenie środka geometrycznego populacji wymaga zsumowania wektorów wierszowych macierzy P i podzielenia każdej współrzędnej wyniku przez rozmiar populacji. Ze względu na reprezentację danych, gdzie kolejne osobniki sa reprezentowane wektorami poziomymi, zastosowanie opisanej w rozdziale 6.5.2 techniki z przesuwajacymi się blokami (tym razem w dół macierzy) byłoby nieefektywne dla sytuacji, gdzie wymiar osobnika jest znacznie mniejszy od rozmiaru populacji. Spowodowane byłoby to utworzeniem małej liczby bloków, których watki maja za zadanie zsumować wiele wartości. Przy wykorzystaniu karty graficznej GTX680 z 8 SMX, jest to marnotrawienie jej mocy obliczeniowej. Został więc zastosowany inny podział pracy niż w przypadku poprzednich jader, który opisany będzie poniżej. Podział na bloki Przy wywołaniu jadra utworzona zostaje dwuwymiarowa siatka, której wielkość zależna jest zarówno od rozmiaru populacji µ oraz wymiaru osobnika n. Jej rozmiar zdefiniowany jest następujacymi równaniami: a = µ/y b = n/x, gdzie a jest liczba bloków w kolumnie, a b ich liczba w wierszu. Oznacza to, że każdej współrzędnej każdego osobnika odpowiadać będzie jeden watek. Sytuację dla bloku o wymiarze 3 3 obrazuje rysunek 6.7.

6.5. Szczegóły implementacji 57 Wykonywane zadania Dla każdego bloku tworzona jest w pamięci współdzielonej macierz o wielkości bloku, w której do właściwej komórki każdy watek zapisuje odpowiadajac a mu wartość współrzędnej x osobnika i. Następnie dokonuje się synchronizacja, aby każdy watek odczytywał właściwe wartości. Watki w ramach jednego bloku sumuja wartości tak utworzonej macierzy pomocniczej wierszami, aż do uzyskania jednego wektora. Każdy z takich wektorów jest składnikiem sumy potrzebnej do obliczenia środka geometrycznego populacji. Aby uniknać synchronizacji bloków (te moga się wykonać w różnej kolejności), po obliczeniu wektora pomocniczej sumy przez blok watków, zostaje on poprzez instrukcję operacji atomowej (opisana w rozdziale 3.1.3) dodany do pamięci globalnej jako wynik częściowy. Ta instrukcja jest używana przy każdym odwołaniu do pamięci globalnej, co eliminuje występujace między blokami zależności. Po zakończeniu dodawania przez wszystkie bloki i podzieleniu tak powstałego wektora przez rozmiar populacji, otrzymujemy środek geometryczny populacji. Tak zastosowany podział zadania na bloki wykonuje się około 5 razy szybciej, niż rozwiazanie z jednowymiarowa siatka analogiczne do opisanego na poczatku rozdziału 6.5.6. 6.5.7. Wybór wielkości bloku Sposób podziału zadań na bloki oraz dobór ich rozmiarów istotnie wpływa na wydajność aplikacji i jest zależny od kilku czynników. W zwiazku z architektura procesorów graficznych oraz sposobem ich działania opierajacym się na osnowach (opisanym w rozdziale 3.1.2), dobrana liczba watków składajaca się na blok powinna być wielokrotnościa rozmiaru osnowy. Utworzona liczba watków w bloku powinna być tak dobrana, żeby multiprocesor miał odpowiednia liczbę osnów do wykonania. Zwiększa to szansę na sytuację, że przy przełaczaniu kontekstu zawsze któraś z osnów jest gotowa do działania, co pozwala ukryć opóźnienia wynikajace z dostępu do rejestrów. Dla jader z dwuwymiarowym blokiem zastosowano poczatkowo typowa wielkość bloku (X, Y ) = (16, 16) dla starszych architektur (np. Fermi). Po weryfikacji empirycznej okazało się, że również dla używanej architektury Kepler rozwiaza- nie to daje najlepsze rezultaty. W przypadku jader o jednowymiarowym bloku, najlepsze rezultaty osiagnięte zostały przy rozmiarze bloku wynoszacym 1024.

7. Porównanie implementacji w języku C++ oraz CUDA C z implementacja wykonana w środowisku EASEA Środowisko EASEA, pomimo swoich licznych zalet zwiazanych z elastycznościa i łatwościa modyfikacji tworzonych aplikacji, generuje kod programu, którego działanie nie wykorzystuje w pełni potencjału dzisiejszych wielordzeniowych kart graficznych, nie mówiac już o platformach zawierajacych ich kilka. W poniższym rozdziale porównane zostana różnice pomiędzy własna implementacja algorytmu ewolucji różnicowej w języku C++ oraz CUDA C oraz aplikacja wygenerowana przez platformę EASEA, które prowadza do takiego stanu. Omówione będa także wady i zalety każdego z tych rozwiazań oraz możliwe drogi do polepszenia ich jakości. Na koniec rozdziału zostana zaprezentowane wyniki testów wydajnościowych wykonanych na własnej implementacji w języku C++ oraz CUDA C i analogicznego rozwiazania wygenerowanego przez platformę EASEA. 7.1. Różnice w wykorzystaniu karty graficznej między EASEA a implementacja w C++ i CUDA C Środowisko EASEA wysyła na urzadzenie podrzędne jedynie zlecenie obliczenia wartości funkcji celu osobników. Implikuje to konieczność transferu całej populacji w każdej generacji. Wszystkie inne operacje wykonywane sa w jednym watku na urzadzeniu nadrzędnym. Ponadto, podczas obliczenia przystosowania pojedynczego osobnika, funkcja celu wykonywana jest wyłacznie przez jeden wa- tek. W takiej sytuacji jedynym zyskiem w porównaniu do implementacji na CPU jest fakt, że równolegle można obliczyć wiele osobników. Napisany w języku C++ oraz CUDA C program wszystkie możliwe operacje zwiazane z algorytmem ewolucji różnicowej wykonuje na karcie graficznej, w pamięci której przechowuje również wymagane dane. Transfer pomiędzy pamięcia urzadzenia podrzędnego a nadrzędnego występuje wyłacznie przy odbiorze poszczególnych wyników - wartości funkcji celu najlepszego osobnika populacji oraz środka geometrycznego. Obliczanie funkcji celu w tej implementacji, omówione w rozdziale 6.5.3, wykorzystuje inny model działania. Jest ono w pełni dostosowane do rozwiazywanego problemu i pozwala na wykorzystanie wielu watków do obliczania wartość funkcji celu tego samego osobnika.

7.2. Wykonane testy wydajnościowe 59 Populacja EASEA Własna implementacja Przyspieszenie 100 24.38 s 0.94 s ok. 26 razy 1000 162 s (2m 42s) 1.53 s ok. 106 razy 10000 1575 s (26m 15s) 6.89 s ok. 229 razy 100000 16910 s (4h 41m 50s) 70.88 s (1m 10.88s) ok. 239 razy Tablica 7.1. Czasy wykonania aplikacji dla wykorzystywanej konfiguracji. 7.2. Wykonane testy wydajnościowe Dla faktycznego zobrazowania różnic w wydajności pomiędzy aplikacja generowana przez środowisko EASEA oraz własna aplikacja wykonano testy. Polegały one na pomiarze czasu wykonania 10000 generacji obu rozwiazań dla wersji klasycznej algorytmu ewolucji różnicowej z uwzględnieniem ograniczeń kostkowych metoda odbijania (pokazane w algorytmie 2.4). Wybrana funkcja celu była funkcja nr 1. z benchmarku CEC2005, która została zaprezentowana w równaniu B.1. W zastosowanych rozwiazaniach obliczany był także środek geometryczny oraz jego przystosowanie. Testy zrealizowane zostały na komputerze klasy PC o następujacej konfiguracji: procesor Intel i7-3770k, 4 3.4 GHz, 8MB cache karta graficzna NVIDIA GTX680 4GB pamięci RAM system operacyjny Debian Wheezy (w momencie tworzenia pracy jest wydaniem testowym tej dystrybucji) w wersji 32-bitowej, z jadrem systemu w wersji 3.5.5-1-686-pae oraz biblioteka CUDA w wersji 4.2 Pomiar czasu wykonania zrealizowany był poprzez zliczenie, ile taktów zegara trwa wykonanie aplikacji (funkcja clock()) i podzielenie tej wartości przez liczbę taktów przypadajac a na sekundę (CLOCKS_PER_SECOND). W zwiazku z tym, wyniki sa powtarzalne i niezależne od obciażenia systemu. Czasy wykonania obu programów zostały zobrazowane na rysunku 7.1. Dedykowana aplikacja przy każdym z testowanych rozmiarów wykazuje znacznie lepsze rezultaty, przy czym zysk jest tym większy, im większa jest populacja. Uzyskane czasy wykonania oraz przyspieszenia zaprezentowane sa także w tablicy 7.1. Rezultaty sa zgodne z oczekiwaniami. Widocznym jest tutaj, że przy populacji o rozmiarze 100, przyspieszenie było 26-krotne. Wraz ze wzrostem populacji osiaga coraz lepsze rezultaty - aż do 239 razy szybciej dla 1000-krotnie większej populacji. W trakcie wykonywania testów możliwe jest napotkanie problemu z działaniem aplikacji przy stosunkowo licznej populacji. Przy inicjalizacji globalnego stanu generatora (rozdział 6.4), dla każdego watku utworzona jest sekwencja liczb. Przy stosunkowo dużym rozmiarze populacji (przy którym odpowiednio

7.2. Wykonane testy wydajnościowe 60 100000 Czas wykonania 10000 iteracji 10000 Czas wykonania [s] 1000 100 10 EASEA Własna aplikacja 1 0.1 100 1000 10000 100000 Rozmiar populacji Rysunek 7.1. Porównanie czasów wykonania aplikacji dla testowanej konfiguracji. zwiększa się liczba tworzonych watków i lokalnych stanów generatora), wymagane było generowanie sekwencji o wysokich numerach, co zgodnie z pozycja [12], jest czasochłonne. W przypadkach używania platform sprzętowych, w których karty graficzna firmy NVIDIA zajmuje się także wysterowywaniem wyjścia monitora, systemy operacyjne uruchamiaja dodatkowy watchdog, którego przepełnienie wynikajace ze zbyt długiego czasu trwania jadra, powoduje przerwanie jego działania w systemie Linux, a zrestartowanie sterownika w systemie operacyjnym Windows. Jednym z rozwiazań problemu jest skrócenie czasu inicjalizacji, które zostało omówione w pozycji [12]. Polega na zmianie sposobu tworzenia globalnego stanu generatora - należy dla każdego watku podać inne ziarno, a te same numery sekwencji. Przy nietrafnym doborze ziaren losowych dla funkcji mieszajacej, która inicjalizuje stan generatora, moga jednak wystapić silne korelacje pomiędzy sekwencjami utworzonymi dla niektórych watków. Takie sytuacje opisane sa jednak jako rzadko występujace. Drugim podanym sposobem na skrócenie czasu inicjalizacji jest podzielenie jadra wykorzystujacego losowe sekwencje na wiele części. Spowoduje to zmniejszenie liczby stanów lokalnych generatora poprzez wielokrotne ich użycie przez wiele jader, jednak znaczaco zmniejsza to czytelność kodu i zwiększa jego zawiłość. W niniejszej pracy dyplomowej udało się znaleźć jeszcze jedno rozwiazanie dla wykorzystywanego do pracy systemu Linux. Polegało ono na uruchamianiu aplikacji przy wyłaczonym graficznym

7.2. Wykonane testy wydajnościowe 61 Biblioteka CUDA EASEA Własna implementacja Przyspieszenie 4.2 16910 s (4h 41m 50s) 70.88 s (1m 10.88s) ok. 239 razy 5.0 16878 s (4h 41m 18s) 55.31 s ok. 305 razy Przyspieszenie 0.2 % 12 % Tablica 7.2. Przyspieszenie działania aplikacji przy użyciu biblioteki CUDA w wersji 5.0. systemie okien (X11). Dla systemu Windows znalezienie rozwiazania okazało się trudne, co częściowo wynika z jego zamkniętej architektury. Po zakończeniu testowania opublikowana została wersja 5.0 biblioteki CUDA. Z racji czasochłonności przeprowadzonych testów, zrezygnowano z ponownego wykonywania całego procesu. Dla uzyskania jednak pewnego pogladu na temat wpływu nowej biblioteki na wydajność działania stworzonych rozwiazań, wykonano test dla populacji o rozmiarze 100000. Reszta parametrów pozostała niezmieniona. Uzyskane wyniki zostały zaprezentowane w tablicy 7.2. Czas działania aplikacji utworzonej przez środowisko EASEA wynosił 16878s (4h 41m 18s), co stanowi przyspieszenie działania zaledwie o 0.2%. Dla własnej implementacji algorytmu ewolucji różnicowej z wykorzystaniem technologii CUDA, czas działania wynosił 55.31s, co dało przyspieszenie o 12%. Pozwala to wnioskować, że kod generowany przez środowisko EASEA powoduje niskie użycie zasobów wykorzystywanej karty graficznej, przez co ewentualne optymalizacje wprowadzone przez nowa wersję biblioteki CUDA sa wręcz niezauważalne. Z racji odnotowanego dodatkowego 12% przyspieszenia dla implementacji w C++ i CUDA C można natomiast stwierdzić, że aplikacja powoduje wysokie zajęcie dostępnych zasobów karty graficznej i stworzona została zgodnie z regułami programowania tego typu urzadzeń. Cały przeprowadzony test pokazuje także, że technologia CUDA jest nadal rozwojowa, skoro modyfikacje wprowadzone wyłacznie do programowej jej części, a bez ingerencji w część sprzętowa, pozwalaja na odnotowanie zysków w działaniu oprogramowania. Podczas opisanego powyżej testowania działania przy wykorzystaniu biblioteki CUDA w wersji 5.0, dla implementacji wygenerowanej przez środowisko EASEA sprawdzono, jak czasy poszczególnych funkcji składaja się na czas wykonania całego algorytmu. Uzyskane wyniki zaprezentowane sa w tabeli 7.3 i dla przejrzystości pokazane na rysunku 7.2. Wyniki pokazuja, że najbardziej czasochłonnym elementem implementacji algorytmu ewolucji różnicowej jest generacja populacji potomnej. Poczatkowo może sprawiać to mylne wrażenie, że jest to główna funkcja, która wymaga optymalizacji. Zaprezentowane czasy wykonania moga znaczaco różnić się w sytuacji zastosowania innej funkcji celu.

7.2. Wykonane testy wydajnościowe 62 Funkcje Procent czasu wykonania Inicjalizacja 3.88 % Generacja populacji potomnej 79.48 % Obliczanie przystosowania osobników 2.02 % Sukcesja elitarna 10.23 % Znalezienie najlepszego osobnika 2.64 % Obliczenie środka geometrycznego i jego przystosowania 1.75 % Tablica 7.3. Udział czasu wykonania poszczególnych kroków algorytmu ewolucji różnicowej w czasie wykonania algorytmu dla kodu utworzonego z wykorzystaniem środowiska EASEA. Rysunek 7.2. Udział czasu wykonania poszczególnych kroków algorytmu ewolucji różnicowej w czasie wykonania algorytmu dla kodu utworzonego z wykorzystaniem środowiska EASEA.

7.2. Wykonane testy wydajnościowe 63 Jadro Procent czasu wykonania Inicjalizacja populacji 0.41 % Inicjalizacja stanów generatora 20 % Generacja populacji potomnej 57.89 % Obliczanie przystosowania osobników 5.31 % Sukcesja elitarna 1.29 % Znalezienie najlepszego osobnika 2.62 % Obliczenie środka geometrycznego i jego przystosowania 12.48 % Tablica 7.4. Udział czasu wykonania poszczególnych jader w czasie wykonania własnej implementacji algorytmu ewolucji różnicowej w języku C++ oraz CUDA C. Rysunek 7.3. Udział czasu wykonania poszczególnych jader w czasie wykonania własnej implementacji algorytmu ewolucji różnicowej w języku C++ oraz CUDA C. Analogiczne testy przeprowadzone zostały dla własnej implementacji wykonanej w języku C++ i CUDA C. Rezultaty zostały pokazane w tablicy 7.4 oraz na rysunku 7.3. Przy implementacji w języku C++ oraz CUDA C zauważalne jest zmniejszenie udziału procentowego czasu trwania procesu generacji populacji potomnej w czasie trwania całego algorytmu z 79.48% do 57.89%, jednak nadal jest on najbardziej znaczacy. Najmniejszy udział procentowy ma proces sukcesji elitarnej, co wynika z eliminacji osobnych kroków redukcji populacji rodziców, redukcji populacji potomnej oraz redukcji finalnej populacji, które sa zawarte w kodzie generowanym przez środowisko EASEA (sa konieczne w działaniu innych rodzin algorytmów ewolucyjnych w nim dostępnych), a które były zbędne w implementacji algorytmu ewolucji różnicowej w języku C++ i CUDA C. Niepokojacy wydaje się znaczacy udział procesu inicjalizacji generatora liczb losowych w czasie wykonania całego algorytmu. Możliwe jest zredukowanie

7.3. Zalety i wady programów 64 go do wartości 1% przy zastosowaniu opisanej wcześniej metody inicjalizacji z wykorzystaniem jednego numeru sekwencji, a różnych ziaren losowych. Może to jednak wpłynać negatywnie na jakość generowanych rozwiazań. Sposobem na zmniejszenie tego czasu mogłoby być przeprojektowanie aplikacji tak, aby wykorzystywała interfejs programistyczny generatora liczb losowych od strony urzadzenia nadrzędnego. 7.3. Zalety i wady programów Zaleta wykonanego w języku C++ oraz CUDA C programu jest niewatpliwie czas wykonania, który przy rosnacej populacji staje się bezdyskusyjnie czynnikiem krytycznym. Jest to jednak program dedykowany dla jednego algorytmu oraz jednego problemu. W przypadku chęci zaimplementowania innej funkcji celu, użytkownik staje przed wyzwaniem zaprojektowania jej wydajnej realizacji w technologii CUDA wraz z pełnym dostosowaniem do zasad tej archmogitektury. Wymagane jest do tego dobre poznanie technologii CUDA, a jakość rozwiazania i czas jego realizacji jest mocno zależny od doświadczenia użytkownika. Jest to zatem program mało wszechstronny, ale nieoceniony w sytuacji, gdy wydajność w wykorzystaniu algorytmu ewolucji różnicowej jest czynnikiem krytycznym. Przedstawione rozwiazanie miało głównie na celu zaprezentowanie możliwości wykorzystania kart graficznych dostępnych w dzisiejszych czasach na rynku pod katem zrównoleglenia algorytmu ewolucji różnicowej. Ewentualny rozwój tego oprogramowania musiałby się opierać na udostępnieniu jednolitej metodyki implementowania w nim nowych funkcji celu. Jednak ze względu na odmienna specyfikę każdego problemu, jaki użytkownicy moga zechcieć rozwiazać przy użyciu algorytmu ewolucji różnicowej, niezwykle trudne wydaje się osiagnięcie pełnego zautomatyzowania procesu generacji implementacji funkcji celu, która zawsze cechowałaby się wysoka wydajnościa. Najbliższe prace powinny polegać na dodaniu obsługi plików konfiguracyjnych, które umożliwia zmiany parametrów algorytmu bez konieczności ponownego kompilowania kodu źródłowego. Pozwoliłoby to także na zaimplementowanie możliwości zmiany wariantu algorytmu ewolucji różnicowej na inne (opisane w rozdziale 2.3). Byłoby to bardzo przydatne i znacznie ułatwiłoby proces testowania zbieżności i szybkości algorytmu przy różnych jego konfiguracjach. Środowisko EASEA, ze względu na brak różnic w wykonywaniu dowolnych funkcji celu, tworzy oprogramowanie mało wydajne. Niezaprzeczalna jego zaleta jest jednak wygoda w użytkowaniu i brak konieczności znajomości technologii CUDA wraz ze sposobami implementacji podstawowych algorytmów z jej użyciem. Pozwala w szybki sposób wytworzyć oprogramowanie rozwiazuj ace problem, którego zdefiniowanie w tej platformie jest znacznie prostsze, niż przy

7.3. Zalety i wady programów 65 własnej implementacji w języku C++ i CUDA C. Jednak przy rosnacej populacji, program utworzony z wykorzystaniem środowiska EASEA staje się mało użyteczny, ze względu na nieakceptowalny czas wykonywania. Dalszy rozwój środowiska EASEA wydaje się także trudny, głównie ze względu na bardzo słaba dokumentację zarówno samej platformy EASEA jak i oprogramowania w niej wykorzystywanego. Wydaje się, że dobrym sposobem na zwiększenie zainteresowania twórców oprogramowania danym środowiskiem mogłoby być przygotowanie do niego pakietów instalacyjnych dla najpopularniejszych dystrybucji systemu Linux. Wymagałoby to doprowadzenia biblioteki LibEASEA (opisanej w rozdziale 4.1.1) do postaci, w której mogłaby być wykorzystywana osobno, bez platformy EASEA, a także zrezygnowania z dołaczania do środowiska statycznie skompilowanych, starych wersji zewnętrznych bibliotek. Konieczne wydaje się wyeliminowanie ze środowiska niszowych i dostępnych wyłacznie na platformę Windows narzędzi AYACC oraz ALex (wspomnianych w rozdziale 4.1.4), a zastapienie ich analogicznymi, wszechobecnymi i wieloplatformowymi rozwiazaniami, np. Bison oraz Flex. Interesujacym pomysłem może wydawać się wzbogacenie środowiska o inne możliwości zrównoleglania. Jedna z nich mogłaby być technologia SSE (Streaming SIMD Extensions) firmy Intel. Jest to jednostka wektorowa, która znajduje się w większości procesorów tego producenta. Pozwala na jednoczesne wykonywanie operacji na poniższych zestawach wartości: dla liczb zmiennoprzecinkowych: 4 liczby pojedynczej precyzji (32-bitowe) lub 2 liczby podwójnej precyzji dla liczb całkowitoliczbowych: 2 liczby 64-bitowe, 4 liczby 32-bitowe, 8 liczb 16-bitowych lub 16 liczb 8-bitowych Rozwiazanie może nie generować tak rewelacyjnych rezultatów jak wykorzystanie kart graficznych firmy NVIDIA, natomiast pozwalałoby na uzyskanie zrównoleglenia operacji dla znacznie tańszych platform sprzętowych, które nimi nie dysponuja.

8. Wnioski W ramach niniejszej pracy inżynierskiej wykonane i przetestowane zostały modyfikacje środowiska EASEA, które umożliwiaja zdefiniowanie parametrów wewnętrznych oraz generację implementacji algorytmu ewolucji różnicowej. Zmiany sa w pełni integralne z dotychczasowa idea tej platformy, zatem utworzone aplikacje moga być wspomagane poprzez oddelegowanie części obliczeń na kartę graficzna lub być w całości wykonywane na jednostce centralnej. Podczas prac wykonana została dogłębna analiza architektury platformy EASEA, która pozwoliła na częściowe jej udokumentowanie oraz zdefiniowanie procedury dodawania do niej nowych elementów. W trakcie tych prac wywnioskowano, że sposób, w jaki środowisko korzysta z programowalnych kart graficznych firmy NVIDIA do wspomagania obliczeń, nie powoduje pełnego wykorzystania dostępnych zasobów. Dla potwierdzenia postawionej tezy, zaproponowane i wykonane zostało własne oprogramowanie w języku C++ oraz CUDA C, dedykowane dla współczesnych kart graficznych. Wykonane pomiary czasu wykonania obu aplikacji pokazały, że zaprezentowany w autorskim programie sposób użycia technologii CUDA pozwolił na osiagnięcie nawet 300-krotnego przyspieszenia w stosunku do idei zastosowanej w środowisku EASEA. Po zakończeniu prac wydana została nowa wersja biblioteki CUDA (5.0). Przeprowadzenie z jej użyciem części testów wydajnościowych pokazało, że w przypadku aplikacji wygenerowanej z użyciem platformy EASEA odnotowano nieznaczne przyspieszenie wynoszace ok. 0.2%. Z kolei dla własnej implementacji algorytmu ewolucji różnicowej w języku C++ oraz CUDA C możliwe było uzyskanie dodatkowo 12 % przyspieszenia. Udowodniło to, że idea wykorzystania karty graficznej przez środowisko EASEA nie prowadzi do pełnego wykorzystania dostępnej mocy obliczeniowej urzadzenia. Pokazuje to także, że technologia CUDA nadal jest rozwojowa, skoro zmiana wyłacznie jej programowych mechanizmów umożliwia tak wysoki wzrost wydajnościowy. W trakcie testów dla nowej wersji biblioteki CUDA wykonano analizę pozwalajac a ustalić, które z elementów algorytmu ewolucji różnicowej zajmuja najwięcej czasu w kontekście całego algorytmu. Zauważono tam, że proces generacji osobników zajmował nawet do 58 % czasu trwania algorytmu. Skupienie się wyłacznie na optymalizacji tej operacji wydaje się jednak błędne, gdyż jej udział

8. Wnioski 67 w czasie trwania całego algorytmu może być zupełnie różny w przypadku zastosowania innej funkcji celu. Dzięki wykonaniu tego rodzaju analizy pokazano również, że prawidłowa inicjalizacja generatora liczb pseudolosowych z biblioteki CURAND zajmuje nawet do 20 % czasu trwania całej aplikacji. Sugeruje to, że ten kierunek wymaga wykonania dalszych prac optymalizacyjnych przez inżynierów firmy NVIDIA. Utworzone w języku C++ oraz CUDA C oprogramowanie, pomimo możliwości uzyskania przez nie znacznego przyspieszenia, co często jest czynnikiem krytycznym, jest narzędziem silnie specjalizowanym wyłacznie na algorytm ewolucji różnicowej oraz jeden zdefiniowany problem. Jego dalszy rozwój powinien być przede wszystkim ukierunkowany na zwiększenie wygody jego wykorzystania, np. wprowadzenie plików konfiguracyjnych. Zagadnienie udostępnienia mechanizmów automatycznego dostosowywania aplikacji dla każdego z możliwych problemów do rozwiazania wydaje się jednak bardzo skomplikowane, a jego realizacja wymagałaby doskonałej znajomości technologii CUDA. Środowisko EASEA, pomimo swojej niskiej, a przy wysokim rozmiarze populacji wręcz nieakceptowalnej wydajności, umożliwia stworzenie w bardzo krótkim czasie oraz w wygodny sposób sparametryzowanego oprogramowania. Jego niezaprzeczalna zaleta jest możliwość wyboru różnych rodzin algorytmów ewolucyjnych, jak również brak konieczności znajomości platformy CUDA. Dobrym pomysłem wydaje się rozszerzenie platformy o możliwości użycia innych technologii pozwalajacych na zrównoleglanie działania aplikacji. Jednak jakość architektury platformy EASEA, prawdopodobnie ze względu na brak usystematyzowania dotychczasowych prac nad nia, odbiega od standardów dzisiejszej inżynierii oprogramowania. Dalszy jej rozwój wymagałby stworzenia szczegółowej dokumentacji, jak również zrezygnowania ze stosowania mało znanych i dostępnych tylko na system Windows narzędzi. Korzystny wpływ na zainteresowanie środowiskiem EASEA mogłaby mieć także jego popularyzacja wśród społeczności najbardziej znanych dystrybucji systemu Linux. Przeprowadzone testy efektywnościowe algorytmu ewolucji różnicowej wykazały, że przy jednakowych parametrach algorytm zachowuje się różnie w zależności od wykorzystanych funkcji celu. Bardzo trudne wydaje się wskazanie powia- zania między jakościa otrzymywanych wyników a wielkościa populacji. Przeprowadzone testy pozwoliły jednak zauważyć, że uzyskiwanie dobrych jakościowo rezultatów wymaga osobnego dostrojenia parametrów algorytmu do rozwiazywa- nego zadania. Testy efektywnościowe pozwoliły zauważyć, że algorytm ewolucji różnicowej w fazie eksploracji pracuje dobrze z dużymi populacjami, natomiast w fazie eksploatacji z małymi. Analiza wpływu zmiany rozmiaru populacji w zależności

8. Wnioski 68 od fazy algorytmu na jakość generowanych rozwiazań mogłaby być podstawa do dalszych prac nad zagadnieniem ewolucji różnicowej.

Bibliografia [1] J. Arabas, Wykłady z algorytmów ewolucyjnych., Warszawa: WNT, 2001 [2] Differential Evolution (DE) for Continuous Function Optimization (an algorithm by Kenneth Price and Rainer Storn). http://www1.icsi.berkeley.edu/~storn/code.html [3] K. V. Price, R. M. Storn, Differential Evolution A Simple and Efficient Heuristic for Global Optimization over Continuous Spaces. http://link.springer.com/content/pdf/10.1023%2fa%3a1008202821328 [4] J. Arabas, A. Szczepankiewicz, T. Wroniak, Experimental Comparison of Methods to Handle Boundary Constraint in Differential Evolution. http://staff.elka.pw.edu.pl/~jarabas/alhe/krakow1.pdf [5] J. Brest, V. Žumer, M. S. Mau cec, Self-Adaptive Differential Evolution Algorithm in Constrained Real-Parameter Optimization http://labraj.uni-mb.si/images/c/cd/janez_cec06.pdf [6] A. K. Qin, P. N. Suganthan, Self-adaptive Differential Evolution Algorithm for Numerical Optimization http://150.214.190.154/eamhco/pdfs/contributionscec05/quin05sad.pdf [7] J. Zhang, A. C. Sanderson, JADE: Adaptive Differential Evolution with Optional External Archive http://150.214.190.154/eamhco/pdf/jade.pdf [8] W. Zhu Massively parallel differential evolution pattern search optimization with graphics hardware acceleration: an investigation on bound constrained optimization problems, 417-437 http://link.springer.com/content/pdf/10.1007%2fs10898-010-9590-0 [9] L. Veronese, R. A. Krohling, Differential Evolution Algorithm on the GPU with C-CUDA http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=5586219 [10] R. Cabido, A. Duarte, A. S. Montemayor, J. J. Pantrigo, Differential Evolution for Global Optimization on GPU http://www2.lifl.fr/meta10/proceedings/meta20100_submission_81.pdf [11] J. Arabas, O. Maitre, P. Collet PARADE: A Massively Parallel Differential Evolution Template for EASEA Swarm and Evolutionary Computation Lecture Notes in Computer Science, 2012, Tom 7269, s. 12-20 [12] CURAND Library http://docs.nvidia.com/cuda/curand/index.html [13] CUDA C Programming Guide v5.0 http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html [14] P. N. Suganthan, N. Hansen, J. J. Liang, K. Deb, Y. -P. Chen, A. Auger, S. Tiwari, Problem Definitions and Evaluation Criteria for the CEC 2005 Special Session on Real-Parameter Optimization. http://web.mysites.ntu.edu.sg/epnsugan/publicsite/shared% 20Documents/CEC2005/Tech-Report-May-30-05.pdf [15] J. Rönkkönen, S. Kukkonen, K. V. Price, Real-Parameter Optimization with Differential Evolution. http: //sci2s.ugr.es/eamhco/pdfs/contributionscec05/ronkkonen05rpo.pdf [16] EASEA platform http://easea.unistra.fr/easea/index.php/easea_platform [17] O. Maitre, GPGPU for Evolutionary Algorithms, Thèses de doctorat, Université de Strasbourg

Bibliografia 70 http://scd-theses.u-strasbg.fr/2456/01/maitre_ogier_2011.pdf [18] Bumble-Bee Software. The Parser Generator Homepage. http://www.bumblebeesoftware.com/

A. Załaczniki do pracy inżynierskiej Zawartość płyty CD: zmodyfikowana wersja środowiska EASEA umożliwiajaca generowanie kodu algorytmu ewolucji różnicowej kod aplikacji w języku C++ oraz CUDA C, umożliwiajacej przetestowanie algorytmu ewolucji różnicowej z wykorzystaniem funkcji 1 z zestawu CEC2005 tekst niniejszej pracy inżynierskiej uzyskane wyniki efektywnościowe algorytmu ewolucji różnicowej (dane i wykresy) skrypty umożliwiajace wygenerowanie w środowisku Octave wykresów z plików z danymi (wykresy.m oraz wykres.m) dokumentacja implementacji algorytmu ewolucji różnicowej w języku C++ oraz CUDA C (plik cec_cuda.pdf) wykorzystywane oprogramowanie Parser Generator (plik ParGen.exe) zawierajace generator lekserów i parserów instrukcja w pliku README.txt

B. Definicje funkcji wykorzystanych w testowaniu efektywności algorytmu ewolucji różnicowej. Poniżej zostana umieszczone definicje tych funkcji z zestawu funkcji testowych CEC2005 (opisany w rozdziale 5.1), dla których wyniki działania algorytmu ewolucji różnicowej zostały omówione w rozdziale 5.4. B.1. Zastosowane oznaczenia f_bias i - przesunięcie funkcji o numerze i = 1, 2, 9, 11, 12, 15 n - wymiar osobnika o = [o 1, o 2,..., o n ] - wektor przesunięcia x = [x 1, x 2,..., x n ] - punkt x - punkt, w którym funkcja osiaga minimum dla wybranej funkcji celu B.2. Funkcja F1: przesunięta funkcja sferyczna f 1 (x) = n zi 2 + f_bias 1 i=1 z = (x o) x [ 100, 100] n f_bias 1 = 450 x = o (B.1) B.3. Funkcja F2: przesunięta funkcja Schwefela f 2 (x) = n i=1 z = (x o) x [ 100, 100] n f_bias 2 = 450 x = o ( i 2 z j ) + f_bias 2 j=1 (B.2)

B.3. Funkcja F2: przesunięta funkcja Schwefela 73 Rysunek B.1. Funkcja f1 z zestawu funkcji testowych CEC2005. Rysunek B.2. Funkcja f2 z zestawu funkcji testowych CEC2005.

B.4. Funkcja F9: przesunięta funkcja Rastrigina 74 Rysunek B.3. Funkcja f9 z zestawu funkcji testowych CEC2005. B.4. Funkcja F9: przesunięta funkcja Rastrigina f 9 (x) = n (zi 2 10 cos(2πz i) + 10) + f_bias 9 i=1 z = (x o) x [ 5, 5] n f_bias 9 = 330 x = o (B.3) B.5. Funkcja F11: przesunięta i obrócona funkcja Weierstrassa f 11 (x) = n ( kmax i=1 k=0 n kmax [a k cos(2πb k (z i + 0.5))])+ k=0 z = (x o)m x [ 0.5, 0.5] n f_bias 11 = 90 a = 0.5, b = 3, k max = 20 x = o Dodatkowe oznaczenia: M - macierz liniowej transformacji [a k cos(2πb k 0.5)] + f_bias 11 (B.4)

B.6. Funkcja F12: zagadnienie Schwefela 75 Rysunek B.4. Funkcja f11 z zestawu funkcji testowych CEC2005. B.6. Funkcja F12: zagadnienie Schwefela f 12 (x) = n (A i B i (x)) 2 + f_bias 12 i=1 A i = n (a ij sin α j + b ij cos α j ) j=1 B i (x) = n (a ij sin x j + b ij cos x j ), i = 1, 2,..., n j=1 (B.5) x [ π, π] n f_bias 12 = 460 x = α Dodatkowe oznaczenia: A, B - macierze n n, z wartościami całkowitymi losowymi z zakresu [ 100, 100] α - wektor wartości losowych z zakresu [ π, π], o wymiarze n B.7. Funkcja F15: funkcja złożona Funkcja F15 jest złożeniem funkcji:

B.7. Funkcja F15: funkcja złożona 76 Rysunek B.5. Funkcja f12 z zestawu funkcji testowych CEC2005. f 1 2 (x): Funkcja Rastrigina f i (x) = n (x 2 i 10 cos(2πx i) + 10) i=1 f 3 4 (x): Funkcja Weierstrassa f i (x) = n ( kmax [a k cos(2πb k (x i + 0.5))]) n kmax [a k cos(2πb k 0.5)] i=1 k=0 a = 0.5, b = 3, k max = 20 k=0 f 5 6 (x): Funkcja Griewanka f i (x) = n x 2 i 4000 n i=1 cos( x i i ) + 1 i=1 (B.6) f 7 8 (x): Funkcja Ackleya 1 n f i (x) = 20 exp( 0.2 n x 2 i ) exp( 1 n i=1 n cos(2πx i )) + 20 + e i=1 f 9 10 (x): Funkcja sferyczna f i (x) = n x 2 i i=1 λ = [1, 1, 10, 10, 5/60, 5/60, 5/32, 5/32, 5/100, 5/100] M i - macierze identycznościowe x [ 5, 5] n f_bias 15 = 120

B.7. Funkcja F15: funkcja złożona 77 Rysunek B.6. Funkcja f15 z zestawu funkcji testowych CEC2005. x = o 1 Powyższe funkcje zapisane sa w postaci podstawowej, bez przesunięcia ani obrotu (x jest zmienna w tej funkcji). Przy obliczaniu wartości funkcji F15, argumentem jest z. Dla funkcji f1 równanie ma postać: f 1 (z) = n (zi 2 10 cos(2πz i) + 10), gdzie z = ((x o 1 )/λ 1 )M 1. i=1