• Nie Znaleziono Wyników

WSPÓLNE PODCIĄGI

Wniosek 7.4. Przyspieszenie wersji z tablicą pośredniczącą w stosunku do wersji sekwencyjnej dla procesora CPU jest

7.6.5. Wyniki eksperymentalne

. (7.26)

7.6.5. Wyniki eksperymentalne

Dla oceny zaproponowanych algorytmów równoległych przeprowadzone zostały ekspery-menty z użyciem następującego zestawu komputerowego:

• CPU: procesor AMD Phenom II X4 810 taktowany zegarem 2600 MHz z 4 MB pamięci podręcznej trzeciego poziomu, 4096 MB RAM (taktowanej 1033 MHz),

• GPU: procesor NVidia GTX 260 taktowany następująco: 696 MHz (jądro), 1501 MHz (mul-tiprocesory), 896 MB pamięci globalnej (taktowanej 1100 MHz) z 27 multiprocesorami (każdy multiprocesor zawiera 8 rdzeni).

Zestaw komputerowy został wybrany w taki sposób, aby wartości rynkowe procesorów CPU i GPU były podobne (jeśli wziąć pod uwagę, że karta graficzna zwiera płytę oraz pamięć glo-balną, to w zasadzie powinno się uwzględnić sumaryczny koszt procesora CPU, płyty głównej oraz pamięci RAM, który jest około dwukrotnie większy niż koszt samego CPU).

Trudno jest zdefiniować jeden sposób porównania wyników uzyskiwanych dzięki zrówno-leglaniu, który byłby akceptowalny przez wszystkich, ponieważ istnieje kilka alternatywnych możliwości, m.in.

• algorytm sekwencyjny dla procesora CPU i algorytm równoległy dla procesora GPU przy podobnej cenie sprzętu – pokazuje jak dobrze algorytmy dla danego problemu zrównole-glają się dla procesora GPU,

• algorytm równoległy dla wielordzeniowego procesora CPU i algorytm równoległy dla pro-cesora GPU przy podobnej cenie sprzętu – pokazuje, dzięki której architekturze (wiele rdze-ni w procesorze CPU czy procesorze GPU) możliwe jest uzyskardze-nie lepszych przyspieszeń,

• algorytm sekwencyjny dla procesora CPU i algorytm hybrydowy dla CPU+GPU używają-cy zarówno dostępnych rdzeni procesora CPU, jak i multiprocesorów GPU – pokazuje, jak wiele można zyskać dzięki zastosowaniu procesora GPU jako akceleratora obliczeń prowa-dzonych z użyciem pełnej mocy obliczeniowej procesora CPU.

Przeprowadzone eksperymenty pozwalają ocenić przyspieszenie dla dwu pierwszych przy-padków. Implementacja algorytmów hybrydowych (trzeci przypadek) jest zwykle trudna, po-nieważ architektury procesorów CPU oraz GPU różnią się w sposób zasadniczy, a przesyły danych oraz synchronizacja pomiędzy CPU i GPU są stosunkowo wolne. Co więcej, zwykle algorytmy dla procesorów GPU działają znacznie szybciej niż równoległe algorytmy dla pro-cesorów CPU i potencjalny zysk ze stworzenia algorytmu hybrydowego nie jest duży. Dlatego

7.6. Algorytmy dla procesorów graficznych 123 też, na podstawie uzyskanych wyników, jedynie dyskutowane są potencjalne korzyści z uży-cia algorytmów hybrydowych. Równoległe algorytmy dla procesorów CPU zostały zaprojek-towane w podobny sposób jak algorytmy dla procesorów GPU, tzn. obliczenia wykonywane są w pudełkach przetwarzanych zgodnie z zasadą drugiej przekątnej.

Badane algorytmy zostały zaimplementowane w języku C++ z użyciem biblioteki CUDA 2.3. Do kompilacji użyto MS Visual C++ 2008 z maksymalną optymalizacją pod kątem pręd-kości. Ciągi testowe zostały wygenerowane z użyciem generatora liczb pseudolosowych o roz-kładzie równomiernym. Wszystkie czasy są medianami ze 101 wykonań. Zamiast czasów abso-lutnych na wykresach pokazane jest przyspieszenie w stosunku do odpowiednich algorytmów sekwencyjnych dla procesorów CPU. Algorytmy na wykresach oznaczane są następująco:

• CPU-s – algorytm sekwencyjny dla procesorów CPU,

• CPU-p – algorytm równoległy dla procesorów CPU (zastosowany procesor CPU zawie-ra 4 rdzenie, jednak wstępne eksperymenty pokazały, że lepiej jest uruchamiać na zawie-raz 32 wątki),

• GPU – algorytm równoległy, oparty na programowaniu dynamicznym, dla procesorów GPU,

• prefetch – algorytm równoległy, oparty na metodzie równoległości bitowej, dla procesorów GPU ze wstępnym wczytaniem wektorów masek,

• preload – algorytm równoległy, oparty na metodzie równoległości bitowej, dla procesorów GPU z tablicą pośredniczącą.

Algorytm oparty na programowaniu dynamicznym

W pierwszym eksperymencie badano algorytm oparty na metodzie programowania dy-namicznego (rys. 7.15). Ponieważ algorytm sekwencyjny równoległości bitowej dla proble-mu LCS jest około 50 razy szybszy niż klasyczny algorytm programowania dynamicznego (dla w = 64), eksperyment ten przeprowadzony został głównie w celu sprawdzenia potencjal-nych możliwości przyspieszenia wyznaczania macierzy programowania dynamicznego za po-mocą metody drugiej przekątnej, w której wykonywanych jest wiele wywołań kodu jądra. Na rys. 7.17a można zaobserwować, że aby korzyści z użycia procesora GPU były znaczące, ciągi powinny być odpowiednio długie. Dla krótkich ciągów liczba pudełek obliczanych równole-gle jest niewielka i niektóre multiprocesory są nieobciążone. Co więcej, dla dłuższych ciągów rozmiary pudełek mogą być większe, dzięki czemu czas obliczania pojedynczego pudełka jest dłuższy i relatywny koszt kolejnych uruchomień kodu jądra jest mniejszy. Przyspieszenie dla algorytmu GPU osiąga wartość 60–70 dla n = m = 105, podczas gdy przyspieszenie dla al-gorytmu CPU-p – wartość 3,8 (bliską teoretycznej wartości 4), czyli ponaddziesięciokrotnie mniej. Tak więc ewentualny algorytm hybrydowy (CPU+GPU) nie działałby znacząco szyb-ciej niżGPU. Rozmiary pudełek dobrano, w trakcie wstępnych eksperymentów, w zależności

10 12 14 16 18 1

2 5 10 20 50 100

GPU

CPU-s CPU-p

log2n = log2m

Przyspieszenie

2 5 10 20 50 100 200 500

1 2 5 10 20 50 100

GPU

CPU-s CPU-p

Rozmiar alfabetu

Przyspieszenie

a)σ= 256 b) n = m = 216

Rys. 7.17. Porównanie przyspieszenia równoległego algorytmu wyznaczania LCS dla procesorów GPU opartego na programowaniu dynamicznym

Fig. 7.17. Comparison of the speedup of the GPU parallel algorithm based on the classical LCS dynamic programming algorithm

od długości ciągów następująco:

bw= max n 27, 26

, bh= min

max m 28, 25

, 27

, bcpuw , bcpuh ∈ [28, 211], gdzie bcpuw oraz bcpuh są odpowiednio długością oraz szerokością pudełka w równoległej imple-mentacji dla procesorów CPU. Ustalenia powyższe gwarantują na tyle dużą liczbę pudełek, że multiprocesory są w miarę równomiernie obciążone, a pudełka nie są zbyt małe (a także nie są zbyt duże z powodu ograniczeń architektury CUDA), co redukuje koszty wielu uruchomień jądra.

Na rys. 7.17b można zauważyć, że dla n = m = 216, przyspieszenia dla dostatecznie dużych alfabetów (σ≥ 20) są zbliżone do siebie i wynoszą ok. 65. Dla mniejszych alfabetów przyspie-szenia są jeszcze większe, z uwagi na stosukowo dużą liczbę dopasowań, co powoduje częstsze wejście do wnętrza instrukcji warunkowej w kodzie dla procesora CPU. Dla procesora GPU spowolnienie spowodowane dużą liczbą dopasowań jest znikome.

Algorytmy równoległości bitowej

W drugim eksperymencie oceniano algorytmy równoległości bitowej (rys. 7.18). Ponie-waż algorytm BP dla problemu LCS jest zwykle najszybszym sposobem wyznaczania pod-ciągu LCS w procesorze CPU, uzyskane wyniki mają znacznie większe znaczenie praktyczne niż wyniki dla algorytmu programowania dynamicznego. Eksperymenty przeprowadzono dla znacznie dłuższych ciągów, ponieważ algorytmy równoległości bitowej w procesorach GPU przetwarzają naraz wgwierszy z odpowiedniej macierzy algorytmu DP. Długości ciągów wej-ściowych ustalono na n = m = 217, 218, 219, 220 dla trzech rozmiarów alfabetu:σ= 4, 20, 128 (rysunki 7.18a–c). Dla małych alfabetów zbadano oba warianty algorytmu równoległości bi-towej dla procesorów GPU. Rozmiary pudełek dobrano, w trakcie wstępnych eksperymentów,

7.6. Algorytmy dla procesorów graficznych 125

17 18 19 20

0 5 10 15

20 preload

prefetch

CPU-s CPU-p

log2n = log2m

Przyspieszenie

17 18 19 20

0 5 10 15 20

preload

prefetch

CPU-s CPU-p

log2n = log2m

Przyspieszenie

a)σ= 4 b)σ= 20

17 18 19 20

0 1 2 3

4 prefetch

CPU-s CPU-p

log2n = log2m

Przyspieszenie

5 10 20 50 100 200

0 5 10 15 20

preload

prefetch

CPU-s CPU-p

Rozmiar alfabetu

Przyspieszenie

c)σ= 128 d) n = m = 220

Rys. 7.18. Porównanie przyspieszenia algorytmu równoległego wyznaczania długości podciągu LCS dla procesorów GPU opartego na równoległości bitowej

Fig. 7.18. Comparison of the speedup of the LCS-length-computing GPU parallel algorithm based on the bit-parallel algorithm

w zależności od długości ciągów następująco:

bw= max n 210, 28

, bh= min

max m 27, 210

, 211 , bcpuw ∈ [213, 214], bcpuh ∈ [214, 215].

Dla małych alfabetów algorytm preload okazał się ponad dwa razy szybszy niż algorytm prefetch. Wskazuje to, że w algorytmie prefetch nie było możliwe ukrycie w pełni wysokich kosztów dostępu do pamięci globalnej. Dlaσ= 4 przyspieszenie algorytmu preload w stosunku doCPU-s wynosi około 20 i rośnie wraz ze wzrostem długości ciągów. Dlaσ= 20 przyspiesze-nie jest mprzyspiesze-niejsze, główprzyspiesze-nie z powodu częstszych chybień w pamięci podręcznej (wektory masek znajdują się w pamięci wspólnej, ale częściej są wymiatane ze związanej z nią pamięci pod-ręcznej). Co więcej, algorytmpreload wymaga znacznie więcej pamięci wspólnej na wektory masek i mniej pudełek może być równocześnie obliczanych w tym samym multiprocesorze, co utrudnia ukrywanie kosztów dostępu do pamięci globalnej. Tym niemniej, przyspieszenia są i tak znacznie większe niż dlaσ= 128, gdzie mógł zostać użyty tylko algorytm prefetch. War-tości przyspieszeń około 4 nie są imponujące, jeśli weźmie się pod uwagę, że procesor GPU

dysponuje 27 multiprocesorami (216 rdzeniami), ale należy pamiętać, że zegar procesora GPU jest około dwukrotnie wolniejszy niż zegar procesora CPU, a także długość słowa komputero-wego procesora GPU jest dwa razy mniejsza niż długość słowa komputerokomputero-wego procesora CPU.

Nawet jednak mimo to można zaobserwować, że koszt dostępów do pamięci globalnej jest du-ży, bo uzyskiwane przyspieszenia są umiarkowane. Przyspieszenia algorytmuCPU-p są bliskie wartości teoretycznej, ale są one porównywalne z przyspieszeniami algorytmów dla procesora GPU tylko dla dużych alfabetów.

Na rys. 7.18d można zaobserwować, jak przyspieszenie maleje wraz ze wzrostem rozmiaru alfabetu. Dla dużych alfabetów czasy działania algorytmów prefetch oraz CPU-p są zbliżo-ne, co powoduje, że uzasadniona może być w takiej sytuacji decyzja o stworzeniu algorytmu hybrydowego (CPU+GPU). Niestety, ilość danych, które muszą być wymieniane między proce-sorami CPU a GPU jest duża, przez co stworzenie wydajnego algorytmu hybrydowego istotnie szybszego od algorytmówprefetch oraz CPU-p jest trudne.

7.7. Podsumowanie

W literaturze można znaleźć wiele prac poświęconych problemowi LCS, co wynika m.in.

z jego istotnych zastosowań praktycznych. W niniejszym rozdziale omówiono tylko najważniej-sze z istniejących algorytmów, które wyróżniają się prostotą (programowanie dynamiczne), du-żą szybkością (algorytmy równoległości bitowej, Hunta–Szymanskiego), małą złożonością pa-mięciową (algorytm Hirschberga), czy też pokazują związek tego problemu z problemem LIS.

Oprócz wielu algorytmów sekwencyjnych dla problemu LCS istnieją także algorytmy rów-noległe. Z uwagi na dużą różnorodność równoległych modeli obliczeniowych wymieniono tylko niektóre z nich, a skupiono się na algorytmach dla procesorów graficznych (GPU). Dziedzi-na wykorzystywania procesorów GPU do obliczeń ogólnego przezDziedzi-naczenia w ostatnich latach bardzo intensywnie się rozwija. W niniejszej pracy zaproponowano (podrozdz. 7.6) dwa algo-rytmy wyznaczania długości podciągu LCS dla procesorów GPU. Pierwszy z tych algorytmów opiera się na metodzie programowania dynamicznego. Z uwagi na dużą prostotę algorytmu se-kwencyjnego oraz niewielkie ilości danych wymienianych z pamięcią globalną procesora GPU przyspieszenia uzyskane dla tego algorytmu w stosunku do wersji sekwencyjnej wyniosły ok.

60–70. Drugim algorytmem zrównoleglonym dla procesora GPU był algorytm równoległości bitowej. Charakteryzuje się on znacznie większą ilością danych wymienianych z pamięcią glo-balną procesora GPU, przez co osiągane przyspieszenia nie były aż tak dobre i zwykle należały do przedziału 5–20. Dla obu tych proponowanych algorytmów przeprowadzono także analizę złożoności czasowej i pamięciowej, dzięki której było możliwe wyznaczenie dla nich teoretycz-nych wartości przyspieszenia.

7.7. Podsumowanie 127 Oprócz podstawowej wersji problemu LCS sporą popularnością cieszą się w literaturze róż-ne jego modyfikacje. W kolejnych rozdziałach poświęcono większą uwagę wybranym warian-tom, a poniżej jedynie wymieniono z nazwy niektóre inne, interesujące, a nieomawiane dalej.

Efektywny algorytm wyznaczania wszystkich podciągów LCS podał Rick w [180]. W proble-mie wyznaczania najdłuższego wspólnego rosnącego podciągu (ang. longest common incre-asing subsequence) oczekiwanym wynikiem jest najdłuższy podciąg dwu lub więcej ciągów, którego wyrazy uporządkowane są rosnąco (ew. niemalejąco) [221, 184, 36, 39]. W proble-mie najdłuższego mozaikowego wspólnego podciągu (ang. mosaic longest common subsequ-ence) [117] danymi wejściowymi są ciąg A, zbiór ciągów

Z

= {Z1, Z2, . . . ZN} oraz liczba k.

Celem jest znalezienie takiej konkatenacji ciągów B = B1B2. . . Bk, gdzie Bi

Z

dla 1 ≤ i ≤ k, aby długość podciągu LCS dla A oraz B była maksymalna. Kolejnymi wariantami są proble-my, w których jeden z ciągów jest traktowany cyklicznie i/lub może być odczytywany z obu kierunków [165].