Wykorzystanie architektury Intel MIC w obliczeniach typu stencil Kamil Halbiniak Wydział Inżynierii Mechanicznej i Informatyki Kierunek informatyka, Rok IV Instytut Informatyki Teoretycznej i Stosowanej Politechnika Częstochowska kamil.halbiniak@wp.pl Streszczenie Obliczenia typu stencil wykonywane są dla n-wymiarowych tablic (najczęściej dwu lub trój wymiarowych), gdzie wartość danego elementu w tablicy wyjściowej obliczana jest na postawie danego elementu znajdującego się w tablicy wejściowej oraz jego sąsiadów. Artykuł przedstawia, sposoby dostosowania algorytmów typu stencil do wielordzeniowej architektury Intel MIC, która w zależności od modelu posiada jednostkę obliczeniową zawierającą od 57 do 61 rdzeni. Do osiągnięcia wysokiej wydajności obliczeń, wykorzystano standard OpenMP, który pozwala na tworzenie wielowątkowych aplikacji oraz zastosowano wektoryzację, która umożliwia wykonywanie kilku operacji zmiennoprzecinkowych w jednym cyklu zegara. Do zoptymalizowania algorytmu wykorzystano również affinity, które umożliwia określenie sposobu przypisania wątków do poszczególnych rdzeni koprocesora Intel Xeon Phi. W artykule zaprezentowano również czasy obliczeń poszczególnych wersji algorytmu wykonanych przez koprocesor oraz porównano je z wynikami otrzymanymi po wykonaniu obliczeń przez procesor hosta. 1 Wprowadzenie W ciągu ostatnich kilkunastu lat zaobserwować można bardzo intensywny rozwój architektur wielordzeniowych. Zjawisko to wynika faktu, iż zwiększenie liczby rdzeni znacząco wpływa na wydajność wykonywanych obliczeń. Rozwój jednostek wielordzeniowych umożliwia tworzenie wielowątkowych aplikacji działających w systemach równoległych. Należy jednak pamiętać, że zrównoleglenie obliczeń nie opiera się tylko na sprzęcie komputerowym. Istotnym aspektem w kwestii programowania równoległego jest dostosowanie algorytmu do wykorzystywanej architektury, tak aby uzyskać maksymalnie wysoką wydajność wykonywanych obliczeń. Zazwyczaj okazuje się, iż implementacja wysokowydajnych algorytmów równoległych jest znacznie trudniejsza w porównaniu z algorytmami sekwencyjnymi. Jednym ze sposobów osiągnięcia wysokiej wydajności jest ograniczenie komunikacji pomiędzy procesorem a pamięcią główną, poprzez efektywne wykorzystanie pamięci cache. Obecnie wiodącym producentem jednostek obliczeniowych opartych o architektury wielordzeniowe jest firma Intel. Doskonałym przykładem tego jest pojawienie się na rynku koprocesora Intel Xeon Phi, opartego o wielordzeniową architekturę Intel MIC. 1
2 Architektura Intel Many Integrated Core Architektura Intel Many Integrated Core to wielordzeniowa architektura wykorzystywana w koprocesorach należących do rodziny Intel Xeon Phi. Wyposażona jest jednostkę obliczeniową składającą się z, w zależności od modelu, od 57 do 61 rdzeni opartych o architekturę x86, taktowanych częstotliwością maksymalnie 1056 MHz [1]. Każdy z rdzeni umożliwia wykonanie 4 wątków sprzętowych, co daje możliwość uruchomienia maksymalnie 244 wątków jednocześnie. Każdy rdzeń posiada własny procesor wektorowy (VPU) oraz trzydzieści dwa 64-bajtowe rejestry, tworząc tym samym nowy zestaw instrukcji wektorowych zwany IMCI [3]. Umożliwiają one wykonanie 16 operacji zmiennoprzecinkowych pojedynczej precyzji oraz 8 operacji zmiennoprzecinkowych podwójnej precyzji w jednym cyklu [2]. VPU wspiera również instrukcje FMA, które pozwalają wykonać dwukrotnie więcej operacji pojedynczej oraz podwójnej precyzji, niż wspomniane wcześniej instrukcje IMCI. Intel MIC nie wspiera instrukcji wektorowych typu MMX, SSE oraz AVX. Każdy rdzeń posiada dwupoziomową pamięć podręczną o rozmiarach odpowiednio 32KB cache L1 oraz 512KB cache L2 [1]. W obu przypadkach jest to 8-drożna pamięć skojarzona o długości linii równej 64 bajty. Rdzenie połączone są ze sobą dwukierunkową magistralą pierścieniową, która umożliwia komunikację z pamięcią główną oraz światem zewnętrznym poprzez magistralę PCI. Magistrala pełni również ważną rolę w tworzeniu spójności pamięci podręcznej poziomu drugiego. Intel MIC posiada w zależności od modelu od 6 do 8 GB pamięci GDDR5. [3]. Intel Xeon Phi umożliwia wykonywanie aplikacji napisanych w trzech językach programowania C, C++ oraz Fortran. Kompilacja kodu źródłowego możliwa jest przy użyciu specjalnego kompilatora stworzonego przez firmę Intel Intel Composer XE [5]. Poniżej zaprezentowano trzy różne sposoby uruchamiania programu: Symetryczny program wykonywany jest jednocześnie przez hosta oraz koprocesor [5]. Obie strony komunikują się ze sobą wykorzystując interfejs umożliwiający przesyłanie komunikatów np: MPI. Odciążeniowy (offload) umożliwia przeniesienie części lub wszystkich z obliczeń z jednego lub kilku wąktów na koprocesor [3]. Dane potrzebne do ich wykonania przesyłane są do koprocesora w trakcie działania programu, który uruchamiany jest po stronie hosta. Natywny program wykonywany jest tylko przez koprocesor. Wymagane jest wykonanie cross kompilacji, aby dostosować aplikację do środowiska operacyjnego koprocesora [5]. 3 Algorytmy typu stencil Algorytmy typu stencil są powszechnie wykorzystywane w skomplikowanych obliczeniach naukowych oraz przetwarzaniu i filtrowaniu obrazów [7]. Algorytm wykonuje operacje na n wymiarowych tablicach (najczęściej dwu lub trój wymiarowych), gdzie wartość danego elementu w tablicy wyjściowej jest obliczana na podstawie danego elementu znajdującego się w tablicy wejściowej oraz jego sąsiadów [7]. Mianem n elementowego stencila określa się punkt oraz n 1 jego sąsiadów. Wyróżnia się różne rodzaje stencili, począwszy od prostych 5 punktowych, aż do bardzo złożonych 27 punktowych. Graficzną prezentację 5 punktowego stencila zaprezentowano na Rysunku 1. 2
Rysunek 1. 5 punktowy stencil W niniejszej pracy wykorzystano algorytm, wykonujący obliczenia przy użyciu 5 punktowych stencili, w którym wartość elementu znajdującego się w tablicy wyjściowej Out na pozycji (i, j) stanowi wartość maksymalną obliczaną na podstawie elementu znajdującego się w tablicy wejściowej In na pozycji (i, j) oraz jego 4 sąsiadów. Pseudokod algorytmu wykorzystanego w niniejszej pracy zaprezentowano na Listingu 1. for(i=0; i<m-1; ++i) for(j=0; j<n-1; ++j) Out[i, j] = Max(In[i, j], In[i+1, j], In[i-1, j], In[i, j+1], In[i,j-1]) Listing 1. Pseudokod algorytmu wykorzytanego w niniejszej pracy 4 Metoda dostosowania algorytmów typu stencil do architektury Intel MIC Sekwencyjny algorytm realizujący obliczenia przy pomocy 5 punktowych stencili zaprezentowano na Listingu 4.1 for(int i=1; i<m_real-1; ++i) for(int j=8; j<n+8; ++j) macierzout[i * n_real + j] = Max(macierzIn[i * n_real + j], macierzin[(i - 1) * n_real + j], macierzin[(i + 1) * n_real + j], macierzin[i * n_real + (j - 1)], macierzin[i * n_real + (j + 1)]); Listing 4.1 Sekwencyjna wersja algorytmu 3
W celu dostosowania algorytmu do architektury Intel MIC, tak aby uzyskać wysoką wydajność obliczeń algorytm zrównoleglono oraz zastosowano wektoryzację. Do zaimplementowania równoległej wersji algorytmu posłużono się standardem OpenMP (ang. Open Multi-Processing). Równoległy algorytm wykonuje obliczenia z wykorzystaniem wszystkich dostępnych zasobów, czyli 240 wątków. Dzięki wykorzystaniu dyrektywy schedule() możliwe było ustawienie sposobu podziału wykonywania pętli przez poszczególne wątki, tak aby kolejny wątek przetwarzał kolejny wiersz macierzy, co zmniejsza ilość odwołań do pamięci, a co za tym idzie zmniejsza czas potrzebny na wykonanie obliczeń. Równie istotne w kwestii przyspieszenia obliczeń, było zaimplementowanie schedulera tak, aby wątki uruchamiane były kolejno na kolejnych rdzeniach. Sposób przypisywania poszczególnych wątków do poszczególnych rdzeni przedstawiono na Rysunku 2. Rysunek 2. Sposób przydzielania wątków do rdzeni Wzrost wydajności obliczeń osiągnięto również poprzez zastosowanie technik wektoryzacji. Do tego celu wykorzystano specjalne funkcje, tak zwane intristic, które umożliwiają wykonanie operacji na 512 bitowych rejestrach. Wraz z wykorzystaniem wektoryzacji, dla macierzy przechowujących dane dodano dodatkowe kolumny, dzięki czemu niezależnie od rozmiaru wiersza uzyskano dopasowanie adresów. 5 Wyniki wydajnościowe Po uruchomieniu sekwencyjnej wersji algorytmu, która wykonana została przez jeden rdzeń dla macierzy M x N, gdzie M = N = 10000 wypełnionej liczbami zmiennoprzecinkowymi podwójnej precyzji, czas obliczeń wyniósł 4.36365 sekundy. W celu zwiększenia wydajności obliczeń, w sekwencyjnej wersji algorytmu wykorzystano instrukcje wektorowe, co sprawiło, że czas potrzebny do wykonania obliczeń zmalał do 0.651203 sekundy. W porównaniu z wersją sekwencyjną bez wektoryzacji osiągnięto przyspieszenie nieco ponad 6.7. Po uruchomieniu zrównoleglonej wersji algorytmu, która wykonała obliczenia przy użyciu 240 wątków, czas obliczeń wyniósł 0.035918 sekundy, co w odniesieniu do algorytmu sekwencyjnego daje przyspieszenie nieco ponad 121.5. Największe przyspieszenie, zarówno względem algorytmu sekwencyjnego jak i równoległego, uzyskano poprzez implementacje technik wektoryzacji w algorytmie równoległym. W porównaniu do algorytmu sekwencyjnego uzyskano przyspieszenie równe 249.6. Jednocześnie algorytm jest o 2.05 raza szybszy, niż algorytm równoległy bez wektoryzacji. W związku z tym, że rdzenie w architekturze Intel MIC oparte są o architekturę x86, możliwa była kompilacja kodu źródłowego na procesorze hosta Intel Xeon CPU E5-2665 2.40GHz. Czasy obliczeń poszczególnych wersji algorytmu dla koprocesora 4
oraz procesora hosta wykonanych dla macierzy wejściowej o wcześniej wspomnianych rozmiarach zestawiono w Tabeli 1. Tabela 1. Zestawienie czasów obliczeń dla koprocesora oraz procesora hosta Algorytm Intel Xeon Phi [s] Intel Xeon E5-2665 [s] Sekwencyjny 4.363650 0.175635 Sekwencyjny + SIMD 0.651203 0.167972 Równoległy 0.035918 0.029958 Równoległy + SIMD 0.017480 0.029708 Graficzną prezentację danych znajdujących się w powyższej tabeli zaprezentowano na Rysunku 3. Rysunek 3. Graficzna prezentacja czasów obliczeń koprocesora oraz procesora hosta Wszystkie obliczenia wykonywane przez procesor hosta zostały dopasowane do jego architektury NUMA (2 CPU x 8 rdzeni). Jak widać w Tabeli 1, w przypadku algorytmu sekwencyjnego wykonywanego na jednym rdzeniu czas potrzebny do wykonania obliczeń przez koprocesor jest znacznie większy, niż w przypadku procesora. Powoduje to, że jest on o 24.84 raza wolniejszy od procesora hosta. Zastosowanie technik wektoryzacji w pewnym stopniu minimalizuje ten problem. W przypadku koprocesora zauważyć można bardzo duży wzrost wydajności, który powoduje, że jest on tylko o 3.87 raza wolniejszy. W przypadku procesora Xeon E5-2665, wykorzystanie instrukcji wektorowych przyniosło minimalny wzrost wydajności. Po zrównolegleniu algorytmu zauważyć można, że czas potrzebny na wykonanie obliczeń przez koprocesor jest niewiele większy i jest on tylko o 1.198 raza wolniejszy. Zastosowanie instrukcji wektorowych w algorytmie równoległym powoduje znaczący wzrost wydajności obliczeń wykonywanych przez koprocesor, w przeciwieństwie do procesora, gdzie wydajność obliczeń podobnie jak w przypadku algorytmu sekwencyjnego wzrasta minimalnie. Wykorzystanie technik wektoryzacji powoduje, że koprocesor jest o 1.7 raza szybszy od procesor hosta. 5
6 Podsumowanie Niniejszy artykuł przedstawia metodę dostosowania algorytmów typu stencil do wielordzeniowej architektury Intel MIC. W tym celu wykorzystano algorytm realizujący obliczenia przy użyciu 5 punktowych stencili, który zaimplementowany został w czterech wersjach: sekwencyjnej, sekwencyjnej + SIMD, równoległej oraz równoległej + SIMD. Na podstawie przeprowadzonych badań, zauważyć można, że pojedyncze rdzenie koprocesora Intel Xeon Phi są nieefektywne, co pokazuje czas obliczeń sekwencyjnej wersji algorytmu. Zwiększenie wydajności obliczeń możliwe jest również poprzez wykorzystanie technik wektoryzacji. Po zastosowaniu instrukcji wektorowych w sekwencyjnej wersji algorytmu czas obliczeń zmniejszył się. Zrównoleglenie algorytmu, tak aby obliczenia wykonane zostały przez 240 wątków, spowodowało znaczące przyspieszenie obliczeń w stosunku do algorytmu sekwencyjnego. Najefektywniejszym rozwiązaniem okazała się równoległa wersja algorytmu, wykorzystująca instrukcje wektorowe. Kod źródłowy algorytmu dostosowano dodatkowo do architektury procesora hosta Intel Xeon E5-2665. Okazało się, że w przypadku algorytmu równoległego wykorzystującego wektoryzację koprocesor jest zdecydowanie szybszy. W pozostałych przypadkach szybszy okazał się procesor hosta. W przyszłości planowany jest dalszy rozwój algorytmu, w celu osiągnięcia jeszcze większej wydajności obliczeniowej. Uzyskanie jej możliwe jest poprzez efektywne wykorzystanie pamięci cache. Wymaga to zaimplementowania schedulera dostępu do danych w celu zredukowania liczby odwołań do pamięci głównej. Bibliografia [1] Intel Xeon Phi Coprocessor System Developers Guide, http://www.microway.com/download/whitepaper/intel_xeon_phi_system_soft ware_developers_guide.pdf [2] Intel Xeon Phi Coprocessor - the Architecture, https://software.intel.com/enus/articles/intel-xeon-phi-coprocessor-codename-knights-corner [3] MIC Architecture, https://www.cac.cornell.edu/vw/mic/architecture.aspx [4] Intel Xeon Phi Core Micro Architecture, http://goparallel.sourceforge.net/wp-content/uploads/2013/07/intel_-xeon-phi- Core-Micro-architecture.pdf [5] Intel Xeon Phi Programming Enviroment, http://goparallel.sourceforge.net/wp-content/uploads/2013/07/intel_-xeon-phi- Programming-Environment.pdf [6] Shah M. Faizur Rahman, Qing Yi, Apan Qasem. Understanding Stencil Code Performance On MultiCore Architectures, http://www.cs.uccs.edu/~qyi/papers/cf11.pdf 6