PROJEKT 3 PROGRAMOWANIE RÓWNOLEGŁE. K. Górzyński (89744), D. Kosiorowski (89762) Informatyka, grupa dziekańska I3

Podobne dokumenty
Przygotowanie kilku wersji kodu zgodnie z wymogami wersji zadania,

ANALIZA EFEKTYWNOŚCI MNOŻENIA MACIERZY W SYSTEMACH Z PAMIĘCIĄ WSPÓŁDZIELONĄ

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

EFEKTYWNOŚĆ MNOŻENIA MACIERZY W SYSTEMACH Z PAMIĘCIĄ WSPÓŁDZIELONĄ

Przykładem jest komputer z procesorem 4 rdzeniowym dostępny w laboratorium W skład projektu wchodzi:

Analiza efektywności przetwarzania współbieżnego. Wykład: Przetwarzanie Równoległe Politechnika Poznańska Rafał Walkowiak Grudzień 2015

Analiza efektywności przetwarzania współbieżnego

Procesory wielordzeniowe (multiprocessor on a chip) Krzysztof Banaś, Obliczenia wysokiej wydajności.

Numeryczna algebra liniowa

Jak zawsze wyjdziemy od terminologii. While oznacza dopóki, podczas gdy. Pętla while jest

Wydajność systemów a organizacja pamięci. Krzysztof Banaś, Obliczenia wysokiej wydajności. 1

Materiały pomocnicze do laboratorium. 1. Miary oceny efektywności 2. Mnożenie macierzy 3. Znajdowanie liczb pierwszych

System obliczeniowy laboratorium oraz. mnożenia macierzy

Literatura. 11/16/2016 Przetwarzanie równoległe - wstęp 1

Wydajność systemów a organizacja pamięci. Krzysztof Banaś, Obliczenia wysokiej wydajności. 1

10/14/2013 Przetwarzanie równoległe - wstęp 1. Zakres przedmiotu

Projektowanie algorytmów równoległych. Zbigniew Koza Wrocław 2012

3. Macierze i Układy Równań Liniowych

Wskaźniki w C. Anna Gogolińska

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Wykład 1_2 Algorytmy sortowania tablic Sortowanie bąbelkowe

MACIERZE. Sobiesiak Łukasz Wilczyńska Małgorzata

Lekcja : Tablice + pętle

Architektura komputerów

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

Równoległy algorytm wyznaczania bloków dla cyklicznego problemu przepływowego z przezbrojeniami

PRZETWARZANIE RÓWNOLEGŁE I ROZPROSZONE. Mnożenie macierzy kwadratowych metodą klasyczną oraz blokową z wykorzystaniem OpenMP.

Programowanie procesorów graficznych GPGPU. Krzysztof Banaś Obliczenia równoległe 1

Zarządzanie pamięcią w systemie operacyjnym

Podstawy OpenCL część 2

Algorytm poprawny jednoznaczny szczegółowy uniwersalny skończoność efektywność (sprawność) zmiennych liniowy warunkowy iteracyjny

Macierze. Rozdział Działania na macierzach

Zadania na zaliczenie przedmiotu Przetwarzanie równoległe Zebrał dla roku.ak. 2015/2016 Rafał Walkowiak,

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

Macierzowe algorytmy równoległe

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

Magistrala systemowa (System Bus)

5. Rozwiązywanie układów równań liniowych

Wstęp do informatyki. Maszyna RAM. Schemat logiczny komputera. Maszyna RAM. RAM: szczegóły. Realizacja algorytmu przez komputer

Przykładowe sprawozdanie. Jan Pustelnik

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

WHILE (wyrażenie) instrukcja;

Schematy blokowe I. 1. Dostępne bloki: 2. Prosty program drukujący tekst.

Wydajność systemów a organizacja pamięci. Krzysztof Banaś, Obliczenia wysokiej wydajności. 1

Architektura potokowa RISC

WHILE (wyrażenie) instrukcja;

Literatura. 3/26/2018 Przetwarzanie równoległe - wstęp 1

System plików. Warstwowy model systemu plików

Równoległość i współbieżność

Równoległość i współbieżność

Programowanie i techniki algorytmiczne

Zapis algorytmów: schematy blokowe i pseudokod 1

Wydajność systemów a organizacja pamięci, czyli dlaczego jednak nie jest aż tak źle. Krzysztof Banaś, Obliczenia wysokiej wydajności.

Algorytm. a programowanie -

Architektura komputera. Cezary Bolek. Uniwersytet Łódzki. Wydział Zarządzania. Katedra Informatyki. System komputerowy

Algorytmy równoległe: ocena efektywności prostych algorytmów dla systemów wielokomputerowych

Badania operacyjne: Wykład Zastosowanie kolorowania grafów w planowaniu produkcji typu no-idle

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

MATERIAŁY POMOCNICZE DO LABORATORIUM Z PRZETWARZANIA RÓWNOLEGŁEGO KWIECIEŃ 2018

Zad. 3: Układ równań liniowych

Pętle. Dodał Administrator niedziela, 14 marzec :27

Analizator wydajności AMD CodeAnalyst

Dodatek B. Zasady komunikacji z otoczeniem w typowych systemach komputerowych

Optymalizacja harmonogramów budowlanych - szeregowanie zadań. Mgr inż. Aleksandra Radziejowska AGH Akademia Górniczo-Hutnicza w Krakowie

Wstęp do informatyki. System komputerowy. Magistrala systemowa. Architektura komputera. Cezary Bolek

Sortowanie zewnętrzne

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Programowanie w Baltie klasa VII

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

1 Układy równań liniowych

dr inż. Jarosław Forenc

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Za pierwszy niebanalny algorytm uważa się algorytm Euklidesa wyszukiwanie NWD dwóch liczb (400 a 300 rok przed narodzeniem Chrystusa).

Podstawy Informatyki Systemy sterowane przepływem argumentów

6. Pętle while. Przykłady

Aproksymacja funkcji a regresja symboliczna

1 Wprowadzenie do algorytmiki

W jakim celu to robimy? Tablica Karnaugh. Minimalizacja

Rozdział 5. Macierze. a 11 a a 1m a 21 a a 2m... a n1 a n2... a nm

METODY ELIMINACJI STUDENTÓW INFORMATYKI. Czyli co student INF-EKA powinien wiedzieć o MESI...

Logiczny model komputera i działanie procesora. Część 1.

Programowanie dynamiczne

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

Rozdział 1 PROGRAMOWANIE LINIOWE

Algorytmy równoległe: ocena efektywności prostych algorytmów dla systemów wielokomputerowych

Algorytmy równoległe. Rafał Walkowiak Politechnika Poznańska Studia inżynierskie Informatyka 2010

Wykład 2. Informatyka Stosowana. 10 października Informatyka Stosowana Wykład 2 10 października / 42

Wykład 2. Informatyka Stosowana. 9 października Informatyka Stosowana Wykład 2 9 października / 42

INFORMATYKA Z MERMIDONEM. Programowanie. Moduł 5 / Notatki

0 + 0 = 0, = 1, = 1, = 0.

Podstawy Informatyki DMA - Układ bezpośredniego dostępu do pamięci

System pamięci. Pamięć wirtualna

Podstawy Programowania C++

Ograniczenia efektywności systemu pamięci

TABLICA (ang. array) pojedyncza zmienna z wieloma komórkami, w których można zapamiętać wiele wartości tego samego typu danych.

Temat 20. Techniki algorytmiczne

Samodzielnie wykonaj następujące operacje: 13 / 2 = 30 / 5 = 73 / 15 = 15 / 23 = 13 % 2 = 30 % 5 = 73 % 15 = 15 % 23 =

Transkrypt:

PROJEKT 3 PROGRAMOWANIE RÓWNOLEGŁE K. Górzyński (89744), D. Kosiorowski (89762) Informatyka, grupa dziekańska I3 17 lutego 2011

Spis treści 1 Opis problemu 2 2 Implementacja problemu 3 2.1 Kod współdzielony........................ 3 2.2 Opis kodu i podejście programowe................ 4 2.2.1 Klasyczne mnożenie macierzy.............. 5 2.2.2 Mnożenie macierzy po transponowaniu......... 7 2.2.3 Wpływ kolejności pętli w mnożeniu........... 10 3 Analiza kodu 12 3.1 Efektywność............................ 12 3.2 Intel Parallel Studio....................... 15 3.3 AMD CodeAnalyst........................ 19 4 Badanie 23 4.1 Koszt współdzielenia i unieważniania danych.......... 23 4.2 Niezamierzone współdzielenie.................. 23 4.3 Przyspieszenie, efektywność, skalowalność............ 23 5 Podsumowanie 24 1

Rozdział 1 Opis problemu Projekt związany jest z zagadnieniem mnożenia macierzy. W jego ramach podejmujemy się analizy przetwarzania równoległego, poprawności kodu, ale także pomiaru zdarzeń procesora wpływających na efektywność. Podejmiemy się wyznaczenia miary efektywności, a także przypieszenia. Przedstawimy optymalny przydział macierzy wynikowej do odpowiednich procesorów. Pokażemy również jaki wpływ na przetwarzanie ma transponowanie macierzy. Spróbujemy ten, mimo wszystko złożony proces badawczy, w sposób zwięzły, poparty odpowiednimi argumentami. 2

Rozdział 2 Implementacja problemu 2.1 Kod współdzielony #pragma omp parallel sections{ #pragma omp section{ for (int z=0; z <Wiersze/2; z++){ for(int u=0; u <Kolumny/2;u++){ for(int k=0; k <Kolumny;k++){ if(transpose) sumtab[z][u]+=tab1[z][k]*tab2[u][k]; else sumtab[z][u]+=tab1[z][k]*tab2[k][u]; #pragma omp section{ for (int z=wiersze/2; z <Wiersze; z++){ for(int u=0; u <Kolumny/2;u++){ for(int k=0; k <Kolumny;k++){ if(transpose) sumtab[z][u]+=tab1[z][k]*tab2[u][k]; else sumtab[z][u]+=tab1[z][k]*tab2[k][u]; 3

#pragma omp section{ for (int z=0; z <Wiersze/2; z++){ for(int u=kolumny/2; u <Kolumny;u++){ for(int k=0; k <Kolumny;k++){ if(transpose) sumtab[z][u]+=tab1[z][k]*tab2[u][k]; else sumtab[z][u]+=tab1[z][k]*tab2[k][u]; #pragma omp section{ for (int z=wiersze/2; z <Wiersze; z++){ for(int u=kolumny/2; u <Kolumny;u++){ for(int k=0; k <Kolumny;k++){ if(transpose) sumtab[z][u]+=tab1[z][k]*tab2[u][k]; else sumtab[z][u]+=tab1[z][k]*tab2[k][u]; 2.2 Opis kodu i podejście programowe Mając na uwadze cel minimalizacji czasu wykonywania wszystkich obliczeń związany z osiągnięciem wyniku mnożenia dwóch macierzy - wynik znajduje się w macierzy sumtab[][] - jedynym słusznym przydziałem zadań do procesorów jest przydział blokowy. Dysponując czterema procesorami podzieliliśmy dane wyjściowe (tablicę wynikową) między procesory. Sposób podziału przedstawiony jest na rysunku 2.1. OpenMP pozwala w łatwy sposób odzwierciedlić ten przydział w kodzie. Jak widać w kodzie zastosowaliśmy cztery bloki sekcji, które odpowiadają poszczególnym ćwiartkom macierzy wynikowej. Dla potrzeb badania zmienne globalne Wiersze oraz Kolumny określające rozmiar macierzy są sobie równe. To pozwala na to by najbardziej wewnętrzna pętla iterowana po zmiennej k przyjmowała wartości od zera do rozmiaru (czyli liczby Wierszy lub Kolumn). 4

Rysunek 2.1: Podział blokowy macierzy wyjściowej. W implementacji wspomogliśmy się również zmienną warunkową, określającą czy mamy do czynienia z podejściem wykorzystującym transponowanie macierzy, czy też mnożymy w klasyczny sposób. 2.2.1 Klasyczne mnożenie macierzy W tym przypadku mnożąc macierz AÖB nie transponujemy macierzy B, zatem w schematyczny sposób każdy z procesorów w podejściu blokowym wykorzystuje połowę wierszy macierzy A i połowę kolumn macierzy B, aby w przydzielonej ćwiartce macierzy wynikowej zamieścić wyniki mnożenia. Przedstawiamy to na rysunku 2.2. Na pewno takie mnożenie jest nieefektywne, z tego względu, że podczas każdego (zależne jest to od rozmiaru macierzy i wielkości Cache procesora) mnożenia danej z macierzy A z daną z macierzy B, następuje ściągnięcie do pamięci podręcznej procesora nowej linii. Dlaczego? W macierzy B odwołujemy się w każdej kolejnej iteracji do nowego wiersza, tym samym do danej, której nie ma w pamięci podręcznej (przy odpowiednio dużej macierzy!). 5

Wpływa to na nieefektywne wykorzystanie pamięci podręcznej, gdyż z każdej ściągniętej linii odczytujemy potencjalnie tylko jedną daną (maksymalnie kilka - nigdy wszystkie). Rysunek 2.2: Schemat mnożenia macierzy (brak transpozycji). Dla przykładu, mając macierz 500Ö500, by osiągnąc w macierzy wynikowej jedną komórkę z wynikiem potrzebny jest dostęp do jednego wiersza macierzy A i 500 wierszy macierzy B. W środowisku w jakim badamy problem długość linii Cache to 64B, a całe Cache posiada 1024 linie. Natomiast dane na jakich 6

operujemy są typu Integer = 4B. Dane potrzebne do mnożenia z macierzy B to 500 linii Cache (64B) razy 16 (tyle linii Cache zajmują dane w jednym wierszu o szerokości 250 kolumn). Jak widzimy jest fizycznie niemożliwe wykorzystanie wszystkich danych macierzy B wykorzystywanych przy mnożeniu podczas jednego pobrania określonej linii do pamięci podręcznej procesora. Poglądowy rysunek utwierdza nas w przekonaniu, że ponowny dostęp do linii PP zawierającej dane z macierzy B następuje po < n n działaniach. Natomiast ściągnięte dane z macierzy A są w kolejnych iteracjach - odwołaniach 2 po kolei odczytywane z Cache, po czym stają się rzecz jasna niepotrzebne i mogą zostać nadpisane. Rysunek 2.3: Poglądowy odzwierciedlenie macierzy w Cache. 2.2.2 Mnożenie macierzy po transponowaniu Pisaliśmy wcześniej o nieefektywnym wykorzystaniu pamięci przez procesory. Teraz nadszedł czas rozwiązania tego problemu. Jak wiemy proste transponowanie macierzy B (musi być to druga macierz!) przebiega w sposób następujący: 7

int i,j; for(i=0; i<wiersze; i++) for(j=0; j i; j++) swap(i,j); gdzie definicja funkcji swap() to: void swap(int i, int j){ int temp = tabb[i][j]; tabb[i][j] = tabb[j][i]; tabb[j][i] = temp; W ramach badania dowiedziemy, że koszt transpozycji macierzy B jest na tyle mały, żeby rzec, iż transpozycja jest opłacalna. Dlaczego warto transponować? Otóż jest to bardzo przydatne, by efektywnie wykorzystać pamięć podręczną procesora. Z uwagi, że teraz mnożenie macierzy A razy B wiąże się z mnożeniem de facto wiersza razy wiersz, więc każda kolejna dana mnożona znajduje się w pamięci podręcznej obok danej pobieranej wcześniej. Inaczej mówiąc ściągnięta linia do Cache zostaje w pełni wykorzystana (na tym etapie zakładamy, że wielkość wiersza macierzy jest wielokrotnością rozmiaru linii Cache procesorów). Obszary macierzy wejściowych i wynikowych jakimi zajmuje się dany procesor przedstawione są na rysunku 2.4. Z uwagi na to, że pamięć podręczna procesora nie jest aż tak diametralnie mała dla małych rozmiarów macierzy czas mnożenia może być porównywalny - mówimy o efekcie utrzymywania danych za całej macierzy B w pamięci podręcznej. Natomiast kiedy macierz osiąga większe rozmiary może być tak, że raz ściągnięta linia (wielkości linii Cache) danych może nie doczekać się ponownego wykorzystania, z uwagi na brak miejsca i bezwzględną konieczność ściągnięcia innych danych w to miejsce. Mówimy tutaj o n2 n2 pobrań do PP. 4 2 8

Rysunek 2.4: Schemat mnożenia macierzy (po transponowaniu macierzy B). Dla każdego procesora taka operacja oznacza tyle, że ściągnięta linia danych do Cache zostaje w kolejnych iteracjach wykorzystywana. Inaczej - każda dana z linii po kolei zostaje odczytywana celem otrzymania wyniku we wskazanej komórce macierzy wynikowej. Nie oznacza to jednak, że wskazana linia z danymi wystarczy, aby była raz pobrana dla zapewnienia wyników we wskazanej ćwiartce macierzy wynikowej. Z uwagi na ograniczenie wielkościowe Cache dla macierzy rozmiaru 180 (180*90/16 1013 linii Cache; 180 kolumn, 90 wierszy, 16 obiektów Integer w linii) potrzeba ponownego ściągania tych samych danych z pamięci operacyjnej do pamięci podręcznej. 9

2.2.3 Wpływ kolejności pętli w mnożeniu Zmiana kolejności pętli iterującej po idenksach wierszów i kolumna w przypadku metody bez transponowania macierzy niesie za sobą na pewno negatywny skutek jeżeli spojrzymy na macierz wynikową. W przypadku macierzy wejściowych zamienią się one tylko charakterystyką - odzwierciedlenie macierzy A w pamięci przyjmie charakterystykę macierzy B i odwrotnie. Natomiast w przypadku macierzy wynikowej może wystąpić masowo zjawisko ponownych pobrań do PP procesora wskazanej linii, w której nastapi modyfikacja (zapis) jednej danej. W przypadku metody wykorzystującej transponowanie skutek in minus będzie dotyczyć tylko macierzy wynikowej. Jeszcze kilka słów odnośnie kolejności samych iteracji w pętlach. Rzeczą pożądaną wydaje się być manualne ustawienie kolejności (innej kolejności) iteracji w pętlach iterujących po wierszach i kolumnach dla każdego z czterech procesorów (w kodzie - sekcji). Dla przykładu - powiedzmy, że procesor P0 i procesor P1 iterują od zerowego wiersza. W takim wypadku pobrania linii z pamięci operacyjnej dotyczące tych samych danych macierzy A mogą się pokrywać czasowo, co wpłynie rzecz jasna na konieczne opóźnienia. Eliminacja tego zjawiska powoduje zrównoleglenie komunikacji na linii pamięć RAM - pamięć Cache procesorów (przesyłanie przez różne porty pamięci). Rysunek 2.5: Przykładowa realizacja kolejności iteracji w pętlach dla każdego z procesorów. 10

Rysunek 2.6: Mnożenie macierzy - odzwierciedlenie w Cache. 11

Rozdział 3 Analiza kodu 3.1 Efektywność Już kilka słów dotyczących efektywności zostało przedstawionych przy opisie dwóch metod obliczeń. Teraz postaram się w sposób bardziej zrozumiały poprzeć założenia konkretnymi liczbami i dowieść prawdziwość przyjętego przydziału blokowego do procesorów. Otóż jeden, konkretny procesor dla wyznaczenia sobie przydzielonej części jednego wiersza macierzy wynikowej sumtab[][] potrzebuje ½liczby kolumn macierzy B i jeden wiersz macierzy A. Po tych obliczeniach wskazany wiersz macierzy A staje się niepotrzebny. Zatem pobierany zostaje następny wiersz macierzy A (kolumny macierzy B są pamiętane), co pozwala przeprowadzić obliczenia i wyznaczyć kolejny wiersz (skrót myślowy - mamy na myśli część wiersza przydzieloną temu procesorowi) macierzy wynikowej. Stąd możemy wyznaczyć wielkość Cache niezbędnej do wykonania obliczeń: ˆ macierz A: 1 wiersz * liczba kolumn * 4B (rozmiar Integer) ˆ macierz B: ½liczby kolumn * liczba wierszy * 4B ˆ macierz C: długość linii Cache procesora (64B) To pozwala wyznaczyć granicę rozmiaru macierzy, dla której w ramach jednego procesora zapewnimy jednokrotne pobranie do pamięci podręcznej danych z tablic A i B a tym samym optymalizujemy liczbę trafień dla podanego podziału pracy. 12

Zgodnie z naszym środowiskiem laboratoryjnym procesor ma do dyspozycji 64kB Cache, więc: 64kB = 64B + n 4B + 1 2 n2 4B 65534B = 64B + n 4B + n 2 2B 65470 = 4n + 2n 2 n < 180 W takim wypadku, zakładając rozmiar macierzy na poziomie n=178 (lub mniej) możemy obliczyć liczbę dostępów do danych w ramach jednego procesora: ˆ macierz A: n3 = 1783 1.345M (każdy element wiersza (n-elementów, 4 4 n-wierszy) wykorzystywany jeden raz dla każdej pobranej kolumny macierzy B ( n-kolumn)) 2 2 ˆ macierz B: n3 = 1783 1.345M (każdy element kolumny (n-elementów, 4 4 n-kolumn) wykorzystywany jeden raz dla każdego pobranego wiersza 2 macierzy A ( n-wierszy)) 2 W założonym przypadku dane są czytane po ich jednokrotnym pobraniu do Cache procesora, a dodatkowo wszystkie dane są wykorzystane. Wnioskując, liczba pobrań linii Cache z pamięci operacyjnej odpowiada liczbie braku trafień. W łatwy sposób możemy wyznaczyć liczbę pobrań z pamięci operacyjnej do Cache: ˆ macierz A: n2 2 = 1782 2 = 15842 obiekty typu Integer ˆ macierz B: n2 2 = 15842 obiekty typu Integer Z uwagi, że linia Cache procesora (64B) zawiera 16 obiektów typu Integer (4B) liczba pobrań wynosi: 15842 2 16 = 1981 Stosunek trafień natomiast wynosi: 15842 2 1981 15842 2 0.9375 Analiza czasów i koncepcji, rozważania nt. problemów mogących wstąpić w realizacji kodu doprowadziły nas do wniosku, że procesory, które wykorzystują do obliczeń te same fragmenty tablic A i B (kolumny lub wiersze) powinny rozpoczynać pracę każdy od innego. Chodzi tutaj o zawartość w potencjalnie innej linii pamięci, co umożliwia komunikację i przesyłanie danej linii do Cache przez różne porty pamięci. Jest to konieczne dla umożliwienia jednoczesnego dla wszystkich procesorów dostępu do pamięci operacyjnej. 13

Ciekawym przypadkiem jest jest macierz wynikowa sumtab[][], której elementy są wynikiem mnożenia elementów macierzy A i B, a w konsekwencji dodawania sum częściowych. Liczba takich dostępów w celu inkrementacji wartości byłaby równa rozmiarowi macierzy, co staje się mało efektywne. Zatem optymalizując wykonanie mnożenia możemy przyjąć iż sumy częściowe będą przechowywane w rejestrach procesora - to doprowadza do jednokrotnej aktualizacji elementu macierzy wynikowej. To ułatwia pracę, bo elementy macierzy wynikowej przydzielone do danego procesora są jednokrotnie sprowadzane do Cache, i wykonywany jest na każdym z elementów linii jednokrotny odczyt i zapis. Dla każdego procesora przypada n2 = 1782 = 7921 elementów macierzy wynikowej. Liczba pobrań danych do Cache to 7921 = 496. Liczba trafień w takim 4 4 16 razie wynosi 2 7921 496 = 15346 (liczba odczytów + liczba zapisów - liczba pobrań). Natomiast stosunek pobrań to 15346 0.9687. 2 7921 Pozostaje nam do przeanalizowania stosunek trafień w stosunku do wszystkich macierzy, a ten wynosi: 15842 2 1981+7921 2 496 0.9479 15842 2+7921 2 14

3.2 Intel Parallel Studio Concurrency Analysis Zmiana kolejności iteracji pętli - optymalizacja dostępu do RAM Rysunek 3.1: Porównanie analiz dla metody wykorzystującej transpozycję (kolor jaśniejszy) i bez transpozycji macierzy B (kolor ciemny) dla różnych rozmiarów macierzy. Na powyższym rysunku widzimy bezpośrednie porównanie, z których możemy odczytać, że przewaga metody wykorzystującej transpozycję jest utrzymana. Przewaga wzrasta wraz ze wzrostem rozmiaru macierzy. Z wykresów przedstawiających oddzielnie poszczególne implementacje możemy odczytać średnie użycie procesora. Stąd dla transpozycji użycie średnie wynosi od 3.78 do 3.84 na rzecz jasna 4 rdzenie. Natomiast dla wersji bez transpozycji mamy spadek zużycia do poziomu 3.17 wraz ze wzrostem rozmiaru macierzy do n=500. //dodać dlaczego tak jest 15

Zmiana kolejności pętli Rysunek 3.2: Porównanie analiz dla metody wykorzystującej transpozycję (kolor jaśniejszy) i bez transpozycji macierzy B (kolor ciemny) dla różnych rozmiarów macierzy. Tutaj godny uwagi jest fakt, że koszt transpozycji macierzy B staje się bardzo wysoki jeśli weźmiemy pod uwagę, że mnożymy macierze małych rozmiarów - ten koszt oczywiście staje się niezauważalny dla większych macierzy, ale wówczas występuje strata w części zrównoleglonej (4 procesory wykonujące obliczenia na przydzielonego bloku). Z czym związany jest taki wzrost kosztu? Transponując macierz B, do uzyskania wyniku w jednej komórce macierzy ostatecznej, potrzebujemy jednego wiersza danych. Oczywiście, jak pisaliśmy wcześniej, rozmieszczenie danych w pamięci jest w tym wypadku bardzo korzystne. Systuacja się zmienia kiedy zamienimy pętle, przez co w konsekwencji pętla iterująca po wszystkich elementach takiego wiersza jest umieszczona na zewnątrz pętli iterującej po kolejnych wierszach. Skutek jest taki, że elementy macierzy wynikowej nie są generowane po kolei (stąd brak możliwości użycia sum częściowych) a jedynie uzupełniane aż obliczenia dla danej pozycji dobiegną końca. To wiąże się z nieoptymalnym wykorzystaniem pamięci i co za tym idzie odpowiednim kosztem. Natomiast korzystniej zamiana pętli wpływa na metodę beztranspozycyjną - po sobie wykorzystywane są elementy macierzy B leżące obok siebie w pobranej linii. Jednak 16

koszt wielokrotnego uzupełnienia wyniku pozostaje. Dlaczego koszt obliczeń na macierzy rozmiaru n=120 nie uległ pogorszeniu? Nie przypadkowo wybraliśmy taki rozmiar macierzy. W dziale 3.1 podaliśmy rozmiar macierzy który przy założonej obsłudze prowadziłby do optymalnego wykorzystania pamięci podręcznej procesorów. Rozmiar ten pozwala na wykorzystanie elementów macierzy B raz pobranych do pamięci podręcznej procesorów. Inaczej mówiąc - liczba obiektów macierzy i ich rozmiar jest mniejsza od wielkości pamięci podręcznej. Zatem wykorzystanie procesora dla metody z transpozycją wzrasta z uwagi na dłuższy czas obliczeń (związany z pobieraniem wielokrotnie dancych do pamięci podręcznej) dla coraz większych macierzy. Natomiast dla małych macierzy, poprzez koszt związany z samą transpozycją (transpozycja wykonywana sekwencyjnie na jednym wątku) to zużycie mieści się pomiędzy 1.0 a 2.5. Klasyczny algorytm wykorzystujący sumy częściowe Rysunek 3.3: Porównanie analiz dla metody wykorzystującej transpozycję (kolor jaśniejszy) i bez transpozycji macierzy B (kolor ciemny) dla różnych rozmiarów macierzy. Obecny rysunek przedstawia niemal takie same wyniki jak poprzedni (dla zamienionych pętli) z tą jednak ważna i oczywistą różnicą, że teraz trans- 17

pozycja jest lepszą metodą dla coraz większych rozmiarów macierzy. Brak zamiany pętli prowadzi do odwrócenia sytuacji. Jednak przyglądając się bliżej wynikom można dojść do jeszcze jednego wniosku. Otóż dla każdej metody czasy tutaj są nieco lepsze od odpowiadającym im czasom z rysunku 3.2. Nie ingerując w kolejność pętli wykorzystać sumy częściowe - jest to możliwe, ponieważ elementy macierzy wynikowej są obliczane w pełni po kolei. Threading Errors i Memory Errors Przeprowadziliśmy inspekcję pamięci, jednak ku uciesze naszych oczu znajwisko wycieku pamięci nie następowało. Podobnie sytuacja miała się z wątkami - Parallel Inspector nie wykrył żadnych błędów. 18

3.3 AMD CodeAnalyst Thread Profiling Zmiana kolejności iteracji pętli - optymalizacja dostępu do RAM Rysunek 3.4: Porównanie profili dla metody wykorzystującej transpozycję (u dołu) i bez transpozycji macierzy B (u góry) dla różnych rozmiarów macierzy. Widzimy, że nie w implementacji nie ustawiono maski powinowactwa - poszczególne wątki nie są na sztywno przypisane do konkretnych rdzeni. Przewaga transpozycji zostaje utrzymana. Sekcje tworzone w bloku zrównoleglonym na rysynkach przedstawiają poszczególne procesory. Rozpoczącie sekcji (wątków) jest niemal jednoczesne - oczekiwanie pozostałych procesorów są niewielkie - podobnie jak zakończenia. Świadczy to o tym, że każdy procesor ma mniej więcej tyle samo pracy do wykonania. Nie występują nielokalne dostępy do pamięci. 19

Zmiana kolejności pętli Rysunek 3.5: Porównanie profili dla metody wykorzystującej transpozycję (u dołu) i bez transpozycji macierzy B (u góry). Profilowanie satysfakcjonujące - w tej metodzie przewagę powinna uzyskać metoda beztranspozycyjna. cechy profilowania identyczne jak poprzednio. Brak nielokalnych, kosztownych dostępów do pamięci. Klasyczny algorytm wykorzystujący sumy częściowe Klasyczna metoda wykorzystująca sumy częściowe i transpozycję pozwala, jak widać bardzo szybko osiągnąć wynik. W profilach nie widać nic niepokojącego. Nie zidentyfikowano żadnych nielokalnych odwołań do pamięci. Na następnej stronie został zobrazowany efekt profilowania. 20

Rysunek 3.6: Porównanie profili dla metody wykorzystującej transpozycję (u dołu) i bez transpozycji macierzy B (u góry). Investigate Data Access Zmiana kolejności pętli oraz klasyczny algorytm wykorzystujący sumy częściowe 21

Rysunek 3.7: Badanie dostępów do danych w pamięci: A) metoda ze zmianą kolejności pętli, B) klasyczny algorytm; Najpierw metoda z bez transpozycji, zaraz pod nią metoda z transpozycją. 22

Rozdział 4 Badanie 4.1 Koszt współdzielenia i unieważniania danych 4.2 Niezamierzone współdzielenie 4.3 Przyspieszenie, efektywność, skalowalność 23

Rozdział 5 Podsumowanie 24