Einführung Haskell Crashkurs Eigenschaften Parallelisieren Ferienakademie - Kurs 1 - parallel Haskell Michael Kerscher 24. September 2009 Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Einführung ◮ Crashkurs Haskell ◮ Eigenschaften der Sprache Parallelisieren ◮ ◮ ◮ Compiler/Laufzeitsystem Haskell Erweiterungen ◮ ◮ Data Parallel Haskell (DPH) Parallel Haskell Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Haskell Crashkurs kurzer Einblick in ◮ Syntax ◮ Lambda-Notation ◮ Currying ◮ Typsystem ◮ Pattern Matching Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Terme und Typangaben Grundlegend besteht Haskell aus den Definitionen von Termen und deren Typangaben x :: Int x = 3 f :: Int -> Int f y = x + y -- T y p d e f i n i t i o n -- F u n k t i o n s d e f i n i t i o n Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Listen, Tupel, arithmetische Listen und Listenkomprehension Es existieren gängige Konstrukte aus anderen Programmiersprachen, z.b. Listen und Tupel [1 ,2 ,3 ,4] :: [ Int ] (1 ,2 ,3.5) :: ( Int , Int , Float ) Als Besonderheit unterstützt Haskell arithmetische Listen und Listenkomprehension -- Liste mit S c h r i t t w e i t e 1 , von 1 bis 9 list = [1 ,2..9] -- Praedikat fuer gerade Zahlen let even n = ( n ‘ mod ‘ 2) == 0 -- alle Elemente aus list mit even = true in [ x | x <- list , even x ] Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching where-Konstrukt Das where-Konstrukt bindet Ausdrücke an Variablen die nur lokal sichtbar sind. Beispiel: f x y = z + z where z = x * y Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Lambda-Notation ◮ Haskell kennt anonyme Funktionen ◮ Anonyme Funktionen z.b. als Parameter für Funktionen höherer Ordnung map (\ y -> 2* y ) [1 ,2 ,3] ◮ -- -> [2 ,4 ,6] folgende Funktionen sind gleichwertig f = \ x y -> x + y g x y = x+y Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Currying ◮ Parameterübergabe ◮ ◮ imperative Sprachen: Tupel (a, b) -> c funktionale Sprachen: Curry-Form a -> b -> c sum :: Int -> Int -> Int sum x y = x + y ◮ ◮ ◮ ◮ Funktionen besitzen nur ein Argument Funktionen können Funktionen generieren und zurückgeben partielle Auswertung von Funktionen möglich Beispiel für die Auswertungsstrategie: a a a a a :: Int = sum 1 2 -- ersten Parameter verwenden = (\ y -> 1 + y ) 2 -- zweiten ----- " - - - - - = 1 + 2 = 3 Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Typklassen Haskell nutzt Typklassen ◮ definieren Funktionen mit bestimmter Signatur ◮ jede Instanz muss alle Funktionen aufweisen Beispieltypklassen: ◮ ◮ Equality-Class class Eq a where (==) , (/=) :: a -> a -> Bool instance Eq ( Float , Float ) where ( x , y ) == ( u , v ) = x == u && y == v v1 /= v2 = not ( v1 == v2 ) Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Vererbung: abgeleitete Typklassen ◮ Ordnungsklasse, abgeleitet von Eq class Eq a = > Ord a where ( <) , ( <=) , ( >=) , ( >) :: a -> a -> Bool ◮ Mehrfachvererbung möglich class ( A a , B a ) = > C a where -- C erbt von A und B ... Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Polymorphie Haskell kennt Polymorphie. ◮ Typvariablen length :: [ a ] - > Int length [] = 0 length ( x : xs ) = 1 + length xs (++) :: [ a ] -> [ a ] -> [ a ] -- L i s t e n k o n k a t e n a t i o n ◮ Typklassen sort :: Ord a = > [ a ] -> [ a ] Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Syntax Lambda-Notation Currying Pattern Matching Pattern Matching und Guards ◮ Pattern Matching erlaubt die Zerlegung von Parametern mittels eines Strukturmusters head ( x : xs ) = x tail ( x : xs ) = xs fst (a , _ ) = a ◮ Wächter sind boolsche Ausdrücke, die am Wert und nicht der Struktur entscheiden ◮ Auswertung der Wächter von oben nach unten. Erster akzeptierter wird ausgeführt. (Laufzeitfehler, falls kein Wächter akzeptiert) sign x | x > 0 | x = 0 | x < 0 = 1 = 0 = -1 Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren lazy evaluation keine Nebeneffekte referenzielle Transparenz Eigenschaften von Haskell Haskell besitzt interessante Eigenschaften, die für Parallelisierbarkeit interessant sind. ◮ lazy evaluation ◮ frei von Nebeneffekten/rein funktional ◮ referenzielle Transparenz Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren lazy evaluation keine Nebeneffekte referenzielle Transparenz Auswertungsstrategie: lazy evaluation ◮ nicht benötigte berechnungsintensive Rechenschritte werden ausgelassen ◮ nicht strikte Sprache. Trotz (1/0) = ⊥, ist folgender Code gültig. f x y = x / 2; -- y wird nicht ausgewert e t a = f 2 (1/0) -- a = 1 ◮ erlaubt das Arbeiten mit unendlichen Datenstrukturen squares n = ( n * n ) : squares ( n +1) -- unendliche Liste two_square s n = take 2 squares n -- durch lazy evaluatio n t e r m i n i e r e n d Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren lazy evaluation keine Nebeneffekte referenzielle Transparenz keine Nebeneffekte ◮ Funktionen verändern keine Daten ◮ ◮ ◮ unveränderliche Objekte (immutable objects) gleiche Funktionsparameter ⇒ gleicher Rückgabewert keine globalen Zustandsvariablen ◮ ◮ Auswertungsreihenfolge irrelevant Mit mehren Prozessoren parallele Ausführung möglich f :: ( Int -> Int ) -> Int -> Int -> Int f g x y = g x + g y Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren lazy evaluation keine Nebeneffekte referenzielle Transparenz keine Nebeneffekte ◮ Variable unveränderbar, sobald Wert zugewiesen wurde ◮ in imperativer Sprache x=x+1 erlaubt ◮ in funktionalen Sprachen kein gültiger Ausdruck (terminiert nicht) ◮ keine Kommunikation über Zustandsvariablen, nur über Rückgabewerte und Parameter ◮ Spezieller Fall: Monaden Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren lazy evaluation keine Nebeneffekte referenzielle Transparenz referenzielle Transparenz ◮ Variablen sind Zeiger auf Berechnungen ◮ Ausdruck muss nur einmalig ausgewertet werden ◮ späterer Zugriff auf den Wert führt nicht mehr zu Berechnungen f :: ( Int -> Int ) -> Int -> Int f n = a + a -- a wird nur einmal berechnet where a = n * n -- ebenso wird n nur 1 x ausgewer te t Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Parallelisieren Verschiedene Möglichkeiten, um Mehrkernprozessoren sinnvoll auszulasten: ◮ implizite parallele Programmierung durch Features des Laufzeitsystems/Compilers ◮ ◮ ◮ explizite parallele Programmierung durch Spracherweiterung ◮ ◮ ◮ bestehende Programme würden davon profitieren Programmierer muss sich keine Gedanken über Parallelisierung machen Programmierer gibt dem Compiler gezielte Anweisungen Es ermöglicht gezielte Eingriffe in den Programmfluss semi-explizite parallele Programmierung Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Laufzeitsystem Der Compiler könnte selbstständig Threads für einzelne Ausdrücke anlegen, da keine Seiteneffekte existieren. Probleme: ◮ ◮ Nicht jedes Programm ist gut parallelisierbar (Bubble Sort) Falls parallelisierbar, ◮ ◮ ◮ muss der Compiler die Aufgaben richtig verteilen können der Aufwand für die Threaderzeugung darf den erreichten Spar-Effekt nicht übersteigen Trotz der Eigenschaften einer funktionalen Sprache ◮ ◮ normale Anwendungen verwenden auch I/O oder auch veränderliche Daten Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Laufzeitsystem Damit der Compiler parallelisieren könnte, sollten folgende Eigenschaften erfüllt sein: ◮ ◮ rein funktionaler Code, keine Seiteneffekte genügend große Berechnungsblöcke, damit kein Leistungsverlust auftritt ◮ ◮ gut: Liste von Primzahlen faktorisieren schlecht: (1+2)*(3+4) Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Data Parallel Haskell Data Parallel Haskell ist der Codename für eine Erweiterung des Glasgow Haskell Compiler und seine Bibliotheken um nested data parallelism zu unterstützen Aus Usersicht ist nur ein neuer Datentyp eingeführt worden: ◮ Parallele Arrays (parallel arrays) die einer Liste ähnlich sind. ◮ Syntaktischer Zucker: statt [ t ] ⇒ [: t :] ◮ [: :] statt [ ] sind auch bei Listenkomprehension möglich weiterhin parallele Versionen von bekannten Funktionen, die mit dem Suffix P gekennzeichnet sind, wie mapP, foldP, filterP, etc. Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Listen im Vergleich mit Parallelen Arrays Es gibt zwei Hauptunterschiede zwischen den beiden Datentypen ◮ Listen ◮ ◮ ◮ ◮ sind nicht strikt, Elemente können bei Bedarf z.b. mit take aus unendlichen Listen geholt werden können induktiv definiert werden haben gerichtete foldR und foldL, die eine Liste von Werten auf einen Wert reduzieren. Parallele Arrays ◮ ◮ ◮ sind strikt. Elemente können parallel verarbeitet werden und müssen immer alle zur Verfügung stehen können nicht induktiv definiert werden haben nur eine ungerichtete foldP Funktion die eine assoziative Funktion als Parameter braucht. Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell nested data parallelism Verschiedene Arten von Datenparallelismus ◮ flat data parallelism ◮ ◮ Daten brauchen spezielle Struktur (gleiche Größe und Tiefe) z.B. von CUDA genutzt mapP (+1) [:1 ,2 ,3 ,4 ,5:] ◮ nested data parallelism ◮ ◮ Unterstützt auch irreguläre Strukturen z.B. Bäume mit unterschiedlich tiefen Ästen, dünnbesetzte Matrizen (im compressed row format) -- kann in einem Takt ausgefue h rt werden mapP ( mapP (+1) ) [:[:1 ,2:] ,[:3 ,4 ,5:] ,[::] ,[:6:]:] Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Beispiel: Quicksort qsort :: Ord a = > [: a :] -> [: a :] qsort [::] = [::] qsort xs = let p = xs !: ( lengthP xs ’ div ’ 2) -- pivot aus Mitte ss = [: s | s <- xs , s < m :] ms = [: s | s <- xs , s == m :] gs = [: s | s <- xs , s > m :] sorted = [: qsort xs ’ | xs ’ <- [: ss , gs :]:] in ( sorted !: 0) +:+ ms +:+ ( sorted !:1) ◮ Die parallele Listenkomprehension sorted ist notwendig, damit die Auswertung parallel laufen kann ◮ Berechnungsstruktur hängt von Vorsortierung ab ◮ Trotzdem wird intern in flat data umgewandelt Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell zu beachten Data Parallel Haskell benötigt in der aktuellen Implementation ein spezielles Prelude (Standardmodule), welche nicht mit dem Standard-Prelude kompatibel ist. ◮ ◮ ganzes Modul entweder normal“ oder parallelisiert ” Für Datenübergabe zwischen den Codeteilen PArray ◮ ◮ konvertierende Wrapperfunktion als Schnittstelle notwendig spezielle Annotation, damit vektorisierter Code nicht in normalen Code eingebettet wird. Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Beispiel mit parallel arrays: Skalarprodukt { - # LANGUAGE PArr , P a r a l l e l L i s t C o m p # -} { - # OPTIONS - fvectorise # -} module where import import import DotP ( dotp_double , d o t p _ w r a p p e r ) qualified Prelude Data . Array . Parallel . Prelude Data . Array . Parallel . Prelude . Double dotp_dou bl e :: [: Double :] -> [: Double :] -> Double dotp_dou bl e xs ys = sumP [: x * y | x <- xs | y <- ys :] -- alternati v e fuer [: x * y | (x , y ) <- zipP xs ys :] d o t p _ w r a p p e r :: PArray Double -> PArray Double -> Double { - # NOINLINE d o t p _ w r a p p e r # -} -- Einbettun g in normalen Code vermeiden d o t p _ w r a p p e r v w = dotp_doub l e ( fromPArray P v ) ( fromPArra y P w) Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Parallel Haskell Parallel Haskell ist wie Data Parallel Haskell eine Erweiterung von Haskell. ◮ Data Parallel Haskell ◮ ◮ v.A. parallele Bearbeitung von ( listenförmigen ) Daten ” ” Parallel Haskell ◮ v.A. Parallelisierung von Algorithmen Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell SMP parallelism vs. Glasgow Parallel Haskell Parallel Haskell existiert in zwei Implementierungen: ◮ SMP parallelism ◮ ◮ ◮ implementiert im offiziellen GHC unterstützt Single Multiprozessor Machines (SMP) Glasgow Parallel Haskell ◮ ◮ wird separat von GHC entwickelt unterstützt SMPs sowie ganze Rechner-Cluster Parallel Haskell ist eine Erweiterung von Haskell ◮ Zwei neue Schlüsselwörter: par und seq ◮ Auswertungsstrategien Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Ein Beispiel für die Anwendung von par und seq -- U r s p r u e n g l i c h e r Fibonacci - Algorith mu s nfib :: Int -> Int nfib n | n <= 1 = 1 | otherwise = n1 + n2 where n1 = nfib ( n -1) n2 = nfib ( n -2) -- Typen von par und seq par :: a -> b -> b seq :: a -> b -> b -- p a r a l l e l i s i e r t e r Fibonacci - Algorith m us import Control . Parallel nfibP :: Int -> Int nfibP n | n <= 1 = 1 | otherwise = par n1 ( seq n2 ( n1 + n2 ) ) where n1 = nfibP (n -1) n2 = nfibP (n -2) Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell par par löst einen spark aus und liefert als Ergebnis das zweite Argument ◮ Ergebnis von par a b = b ◮ ◮ a wird in eine Queue gegeben, b wird sofort berechnet par n1 (seq n2 (n1+n2))= seq n2 (n1+n2) ◮ n1 wird berechnet, wenn Kapazität verfügbar Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell seq seq zwingt die Laufzeitumgebung, den ersten Parameter vor dem Zweiten auszuwerten. ◮ Ergebnis von seq a b = b. Aber a wird vor b berechnet. ◮ ◮ ◮ seq n2 (n1 + n2)= n1 + n2 Zuerst wird n2 ausgewertet Erst wenn n2 fertig ist, wird n1 + n2 berechnet Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Strategien ◮ Haskell verwendet lazy-evaluation. Probleme: ◮ ◮ ◮ ◮ Berechnungen im spark“-Ausdruck werden nicht vollständig ” ausgewertet Workaround: forcing function, welche Berechnung erzwingt. Nachteil: Viele solcher Funktionen notwendig, Lesbarkeit verschlechtert Auswertungsstrategien beseitigen dieses Problem ◮ ◮ gezielte Auswertung möglich Strategien können kombiniert werden Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Grundstrategien ◮ type Strategy a = a -> () ◮ r0: keine Auswertung des Ausdrucks r0 :: Strategy a r0 _ = () ◮ ◮ Nützlich bei Auswertung eines Tupels, wenn nur ein Teil notwendig ist. rwhnf: Auswertung bis weak head normal form rwhnf :: Strategy a rwhnf x = x ‘seq ‘ () Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell komplexere Strategien ◮ Grundstrategien nur für Grunddatentypen ◮ komplexere Datenstrukturen (Liste und Tupel) class NFData a where rnf :: Strategy a rnf = rwhnf -- Typklasse instance NFData a = > NFData [ a ] where rnf [] = () rnf ( x : xs ) = rnf x ‘ seq ‘ rnf xs instance ( NFData a , NFData b ) = > NFData (a , b ) where rnf (x , y ) = rnf x ‘ seq ‘ rnf y Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Beispiel für Strategien using wendet eine Strategie auf Werte an: using :: a -> Strategy a -> a using x s = s x ‘ seq ‘ x Quicksort in einer parallelen Version unter Verwendung einer Strategie: quicksort S ( x : xs ) = losort ++ ( x : hisort ) ‘ using ‘ strategy where losort = quicksortS [ y | y <- xs , y < x ] hisort = quicksortS [ y | y <- xs , y >= x ] strategy result = rnf losort ‘par ‘ rnf hisort ‘par ‘ rnf result Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell Einführung Haskell Crashkurs Eigenschaften Parallelisieren Laufzeitsystem Data Parallel Haskell parallel Haskell Vielen Dank für die Aufmerksamkeit. Michael Kerscher Ferienakademie - Kurs 1 - parallel Haskell