Zastosowanie technologii nVidia CUDA do zrównoleglenia

advertisement
Zastosowanie technologii nVidia CUDA do zrównoleglenia
algorytmu genetycznego dla problemu komiwojażera
Adam Hrazdil
Wydział Inżynierii Mechanicznej i Informatyki
Kierunek informatyka, Rok V
[email protected]
Streszczenie
W poniższej pracy zaprezentowano możliwości przetwarzania równoległego
przy użyciu wielu rdzeni procesora graficznego na przykładzie problemu
komiwojażera realizowanego przez algorytm genetyczny. Genetyczny algorytm
został zaimplementowany w dwóch wersjach: sekwencyjnej działającej na CPU
oraz równoległej z wykorzystaniem technologii CUDA. Zaprezentowana została
architektura CUDA, porównano wyniki otrzymane przez dwie wersje algorytmu
pod względem wydajności oraz sformułowano wnioski.
1 Wstęp
Spośród wielu metod sztucznej inteligencji obliczeniowej algorytmy genetyczne AG
(ang. Genetic Algorithms) doczekały się wielu zastosowań. Można je wykorzystywać
do rozwiązywania rzeczywistych problemów z dowolnej dziedziny ludzkiej
działalności.
Rozwój algorytmów genetycznych został zapoczątkowany w roku 1975 pracą
Johna Hollanda z Uniwersytetu Michigan [9]. Obecnie zastosowanie algorytmów
genetycznych jest imponujące, stosowane są bowiem one w szeregowaniu zadań,
modelowaniu finansowym, optymalizacji funkcji oraz harmonogramowaniu.
Gwałtownie przeżywany rozwój algorytmów genetycznych pobudził w ostatnim czasie
różne obszary nauki i techniki min. fizykę, biologię, programowanie układów układów
scalonych VLSI (ang. Very Large Scale of Integration), a także zarządzanie. Rozróżnia
się trzy zasadnicze grupy zastosowań AG [4]: algorytmy przeszukujące (Search),
optymalizujące (Optimization) i uczące (Learning). Algorytmy genetyczne wykorzystują
poszukiwania oparte na mechanizmach doboru naturalnego oraz dziedziczności. Łączą
w sobie ewolucyjną zasadę przeżycia najlepiej przystosowanych osobników z
systematyczną, choć zrandomizowaną wymianą informacji [2].
Algorytmy ewolucyjne i genetyczne są ściśle zdefiniowanymi procedurami
matematycznymi wykorzystywanymi do rozwiązywania niektórych problemów, dla
których standardowe algorytmy są nieznane lub mają nieakceptowalnie długi czas
1
działania. Zasada ich działania opiera się na mechanizmach skonstruowanych w oparciu
o procesy zaobserwowane w biologicznej ewolucji.
Zadanie komiwojażera (TSP - ang. Traveling Salesman Problem) [10] jest
właśnie jednym z takich problemów. Ogólny problem komiwojażera sformułowany jest
następująco: dane jest n miast, a każde dwa z nich połączone są drogą o określonej
długości. W jednym z miast znajduje się komiwojażer, który chce odwiedzić wszystkie
miasta w taki sposób, aby w każdym mieście znaleźć się dokładnie jeden raz, a na
koniec wędrówki powrócić do miejsca startowego. Optymalnym rozwiązaniem jest
trasa, dla której koszt objazdu jest najmniejszy.
Na podstawie teorii grafów problem komiwojażera można przedstawić jako
zadanie polegające na znalezieniu minimalnego cyklu Hamiltona w pełnym grafie
ważonym. Miasta są wierzchołkami grafu, trasy pomiędzy nimi to krawędzie z wagami.
Waga krawędzi może odpowiadać odległości pomiędzy miastami połączonymi
tą krawędzią, czasowi podróży lub koszcie przejazdu.
Zadanie sprowadza się do znalezienia permutacji miast (i1, i2,…, in), dla której
koszt jest najmniejszy. Dla problemu komiwojażera nie jest znany algorytm efektywny,
tzn. rozwiązujący ten problem w czasie wielomianowym. Należy on do klasy
problemów NP-trudnych, w związku z tym niemożliwy jest przegląd zupełny
przestrzeni rozwiązań. Dlatego jest często wybierany do weryfikacji nowych pomysłów
w dziedzinie AG.
2 Ogólna charakterystyka architektury CUDA
CUDA (ang. Compute Unified Device Architecture) [6] to technologia typu GPGPU
(ang. General-Purpose Computing on Graphics Processing Units) opracowana przez
firmę nVidia. Jest uniwersalną architekturą procesorów wielordzeniowych (głównie kart
graficznych) umożliwiającą wykorzystanie ich mocy obliczeniowej do rozwiązywania
ogólnych problemów numerycznych w sposób wydajniejszy niż w tradycyjnych,
procesorach ogólnego zastosowania (CPU).
Na rysunku 1 przedstawiono porównanie mocy obliczeniowych kolejnych
generacji procesorów typu CPU firmy Intel oraz GPU firmy nVidia.
2
Integralną częścią architektury CUDA jest oparte na języku programowania
C środowisko programistyczne wysokiego poziomu, w którego skład wchodzą
m.in. specjalny kompilator (nvcc) oraz interfejs programowania aplikacji. Projekt
architektury CUDA zakłada pełną skalowalność programów, napisany dziś program
wykonywalny ma w przyszłości działać bez żadnych zmian na coraz wydajniejszych
procesorach graficznych posiadających coraz większą liczbę rdzeni, rejestrów, pamięci
operacyjnej i innych zasobów.
Procesor (ang. Host) wykonuje kod sekwencyjnie do czasu wywołania funkcji
jądra (ang. Kernel) działającej na karcie graficznej (ang. Device). Każdorazowe
wywołanie tej funkcji tworzy na karcie graficznej nową siatkę wątków (ang. Grid).
Wątki są zgrupowane w blokach (możliwa struktura 1D, 2D, 3D), maksymalnie można
przydzielić 512 x 512 x 64 wątków w bloku. Bloki zgrupowane są w siatce,
maksymalnie 65535 x 65535. Wymiary bloku i siatki podajemy jako argumenty
wywołania funkcji jądra. Schemat wywołania funkcji jądra wywołanej przez procesor
został przedstawiony na rysunku 2.
Rys. 2. Schemat wywołania funkcji jądra (źródło [5])
Karta graficzna jest zrealizowana jako zbiór multiprocesorów (SM, ang. Single
Multiprocessor), które są podstawową jednostką obliczeniową GPU. Są to skalarne,
zmiennoprzecinkowe jednostki arytmetyczno-logiczne pojedynczej precyzji. Każdy
multiprocesor
zawiera
osiem
równoległych
procesorów
strumieniowych
(SP, ang. Scalar Processor). SM zawiera również dwie jednostki specjalne
(SFU, ang. Special Function Units), wykonujące bardziej złożone funkcje
3
matematyczne (np. pierwiastek, funkcja sinus). Każdy SM posiada także jedną
jednostkę arytmetyczną podwójnej precyzji. Celem ułatwienia współpracy między
wątkami wykonywanymi przez SP każdy SM zawiera 16 KB pamięci współdzielonej.
Każdy multiprocesor ma zlecane do jednoczesnego wykonania jeden bądź więcej
bloków wątków.
3 Implementacja algorytmu
Algorytmy z wykorzystaniem technologii CUDA zostały zaimplementowane tak, aby
sposób działania najwierniej odwzorowywał wersje sekwencyjne wykonywalne na CPU.
Każda iteracja algorytmu jest jednorazowo wywołaną funkcją jądra. Jest to
spowodowane tym, że przed rozpoczęciem nowej iteracji wszystkie operacje w
poprzedniej iteracji muszą się zakończyć. Biblioteka CUDA podczas działającej funkcji
jądra nie umożliwia synchronizacji bloków, dlatego nie można rozpocząć obliczeń dla
nowej iteracji w tej samej funkcji jądra. Ponowne wywołanie funkcji jądra w tym
samym wątku programu wykonywalnego na CPU daje gwarancje zakończenia działania
wszystkich bloków. Populacja między wywołaniami funkcji jądra jest przechowywana
w pamięci głównej GPU, która jest zachowywana między kolejnymi wywołaniami
funkcji jądra do czasu jej jawnego zwolnienia.
Opracowanie algorytmu genetycznego pod kątem odpowiedniego zadania
wymaga
zaprojektowania
odpowiedniego
kodowania
dla
chromosomów.
W implementacji algorytmu zastosowana została reprezentacja ścieżkowa [1].
Krzyżowanie w przypadku reprezentacji ścieżkowej jest skomplikowaną
czynnością. Wynikiem działania operatora krzyżowania musi być potomek
akceptowalny (zawierający unikalną listę miast). Dla przyjętej reprezentacji
zastosowano krzyżowanie z częściowym zastępowaniem [1] (ang. Partially Matched
Crossover).
Wynikiem działania operatora mutacji tak jak w przypadku krzyżowania, musi
być chromosom akceptowalny. Dla chromosomu w zastosowanej reprezentacji
zaimplementowano następujące operatory mutacji:
•
wzajemnej wymiany – wymienia się dwa losowe miasta,
•
inwersją – obraca się kolejność wszystkich genów w chromosomie,
•
przesunięciem – przesuwa się chromosom w wybraną stronę, końcowe geny
które wychodzą poza zakres wstawiane są na początek,
•
lokalna optymalizacja - wybiera się miasto i zamienia sąsiadem, jeśli wynik
zamiany przynosi skrócenie trasy.
W selekcji osobników naśladuje się mechanizm przeżycia w naturze. Wobec
tego oczekuje się, że osobnik o najwyższym stopniu przystosowania uzyska liczne
potomstwo. Do najważniejszych metod selekcji należą [3]:
•
metoda elitarna,
•
metoda turniejowa,
•
metoda proporcjonalna ("Metoda koła ruletki ").
Ocena danego osobnika polega na zsumowaniu odległości pomiędzy kolejnymi
miastami wyznaczonymi przez daną reprezentację. Mniejsza wartość oceny jest tożsama
z mniejszym kosztem odwiedzenia wszystkich miast.
4
4 Porównanie wydajności algorytmów
Testy przeprowadzane były na komputerze wyposażonym w procesor
AMD Phenom™ II X4 B50 taktowany rdzeniem 3,1 GHz, pamięć RAM DDR 3
taktowaną zegarem 1333 MHz oraz kartę graficzną GeForce GTX 275, posiadającą
30 multiprocesorów, taktowanych zegarem 1,40 GHz. Testy były prowadzone w
środowisku Windows 7 Professional 64 bit. Kod był kompilowany za pomocą CUDA
SDK 3.0 beta i Visual Studio 2008.
Algorytmy zostały przetestowane dla wielkości zadania 500 miast. Miasta
zostały losowo umieszczone na powierzchni o wymiarach 1200 x 850. Przykładowy
chromosom populacji początkowej został pokazany na rysunku 3.
Rys.
populacji początkowej
Wyniki porównywania obu algorytmów wykazały zbliżone długości tras dla
analogicznych pokoleń. Wyniki zostały zaprezentowane na rysunku 4.
Rys. 4. Porównanie przystosowania najlepszego osobnika algorytmów w kolejnych
iteracjach algorytmu
5
Dla oceny wydajności algorytmów z wykorzystaniem różnych technologi wyniki
testu przedstawiono na rysunku 5 jako wykres zależności przystosowania od czasu
działania aplikacji.
Rys. 5. Porównanie przystosowania najlepszego osobnika algorytmów w czasie
Wyraźnie widoczny jest duży wzrost wydajności aplikacji korzystającej
z technologii CUDA. Po 10 sekundach aplikacja wykorzystująca technologię CUDA
i osiągnęła wynik lepszy od wersji sekwencyjnej po czasie 10 minut. Ostateczny wynik
działania aplikacji wykorzystującej technologię CUDA został pokazany na rysunku 6.
Osiągnięcie tego wyniku wymagało 60 tysięcy pokoleń. W tym czasie sekwencyjna
aplikacja uzyskała 3 razy dłuższą trasę wykonując 1400 pokoleniach. Wyniki w
testowanym przypadku wykazują 42 krotne przyśpieszenie.
Rys. 6. Wynik działania algorytmu CUDA
(najlepszy wynik w 60000 pokoleniu)
Większa ilość miast wiąże się także ze zwiększeniem operacji wykonywanych
na chromosomach. Ilość możliwych rozwiązań w przypadku większej ilości miast jest
6
też znacznie większa. Następnie badano wpływu rozmiaru chromosomu na czas
wykonywania aplikacji. Wyniki testów zostały przedstawione na rysunku 7.
Rys. 7. Wpływ rozmiaru chromosomu na czas działania aplikacji wykonującej
technologie CUDA
Porównanie czasów wykonywania obu aplikacji przedstawiono na rysunku 8.
Badano
przyśpieszenia w zależności o rozmiarów chromosomu. Maksymalne
osiągnięte przyśpieszenie w tym przypadku osiągnięto dla 400 miast i czas
wykonywania aplikacji z wykorzystaniem technologi CUDA był 58,6 razy krótszy. Dla
chromosomów o długości większej od 320 wartości przyśpieszenia osiągnęły podobny
poziom. Badając zachowanie algorytmu dla różnych długości chromosomów, testowano
wydajność równoległych operatorów genetycznych.
Rys. 8. Wpływ rozmiaru chromosomu na przyśpieszenie
Aplikacje poddano testom badającym wpływ rozmiaru populacji na
przyśpieszenie w porównaniu z wersją sekwencyjną wykonywalną na CPU. Pierwszy
test przeprowadzono dla 512 osobników, następnie zwiększano w każdym następnym
teście dwukrotnie rozmiar populacji, aż do wielkości 131072 osobników. Zwiększanie
parametru rozmiaru populacji wprowadza większy potencjał genetyczny, ale ma też
swoje minusy, prowadzi do wydłużenia czasu obliczeń, w wersji działającej na CPU
czas działania zwiększa się proporcjonalnie do wielkości populacji. W przypadku
wykorzystania karty graficznej zwiększenie ilości populacji także wpływa na
zwiększenie czasu działania programu, jednak w testowanej aplikacji nie zawsze są one
proporcjonalne do wielkości populacji. Wyniki zostały przedstawione na rysunku 9.
7
Rys. 9. Wpływ rozmiaru populacji na przyśpieszenie
W wersji wykorzystującej CUDA jedno pokolenie jest jednorazowo wywołaną
funkcją jądra. Wywołanie funkcji jądra przez komputer i przekazanie jej parametrów
jest związane z pewnymi nakładami czasowymi. W przypadku mniejszych populacji ma
to większe znaczenie, ponieważ nakład czasowy jest czynnikiem determinującym czas
działania, zwiększając rozmiary populacji zwiększamy ilość obliczeń dla funkcji jądra
a tym samym maleje znaczenie kosztów jej wywołania.
5 Podsumowanie
W artykule opisano podstawowe założenia architektury CUDA. Na przykładzie
algorytmów genetycznych wykazano istotną przewagę układów GPU nad klasycznym
CPU w przypadku przeprowadzania obliczeń o znacznym stopniu równoległości.
Otrzymane wyniki świadczą, że architektura CUDA świetnie sprawdza się w dziedzinie
algorytmów genetycznych. Znacznie większą wydajność w stosunku do ceny oraz
ogólnodostępność tej technologii mogą zacznie przyczynić się do rozwoju algorytmów
ewolucyjnych.
Bibliografia
[1] Z. Michalewicz, Algorytmy genetyczne + struktury danych = programowanie
Ewolucyjne, wydanie III, Warszawa: WNT, 2003.
[2] D.E. Goldberg, Algorytmy genetyczne i ich zastosowania, Warszawa: WNT, 1998.
[3] J. Arabas: Wykłady z algorytmów ewolucyjnych. Warszawa: WNT, 2001.
[4] H. Kwaśnicka, Obliczenia ewolucyjne w sztucznej inteligencji, 1999
[5] D. L. Applegate, R. E. Bixby, V. Chvátal, W. J. Cook, The Traveling Salesman
Problem: A Computational Study, 2006.
[6] NVIDIA Corporation, NVidia CUDA Programming Guide 2.3, 2009.
[7] NVIDIA Corporation, GeForce 8800 GPU Architecture Overview, 2006 .
[8] NVIDIA Corporation, Advanced CUDA Training, 2008
[9] J. H. Holland, Adaptation in Natural and Artificial Systems, University of Michigan Press,
1975.
[10] T. H. Cormen, C. E. Leiserson, R. L. Riverst, Wprowadzenie do algorytmów
Warszawa WNT, 2002.
8
Download