diff options
| -rw-r--r-- | ws2015/ffp/blaetter/08/FFP_U08_Parallel.hs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/ws2015/ffp/blaetter/08/FFP_U08_Parallel.hs b/ws2015/ffp/blaetter/08/FFP_U08_Parallel.hs new file mode 100644 index 0000000..8615dad --- /dev/null +++ b/ws2015/ffp/blaetter/08/FFP_U08_Parallel.hs | |||
| @@ -0,0 +1,219 @@ | |||
| 1 | -- Fortgeschrittene Funktionale Programmierung, | ||
| 2 | -- LMU, TCS, Wintersemester 2015/16 | ||
| 3 | -- Steffen Jost, Alexander Isenko | ||
| 4 | -- | ||
| 5 | -- Übungsblatt 07. 9.12.2015 | ||
| 6 | -- | ||
| 7 | -- Thema: Paralleles Rechnen | ||
| 8 | -- | ||
| 9 | -- Hinweis: | ||
| 10 | -- Falls Ihr Rechner nur 1--2 echte Prozessorkerne besitzt, | ||
| 11 | -- dann loggen Sie sich für diese Übungen in einem Rechner am | ||
| 12 | -- CIP-Pool der Informatik ein. | ||
| 13 | -- Informationen zum Remote-Login am CIP-Pool finden Sie auf: | ||
| 14 | -- http://www.rz.ifi.lmu.de/FAQ/index.html | ||
| 15 | -- Abschnitt "Von zu Hause/remote aus ..." | ||
| 16 | -- Prüfen Sie auch, dass der gewählte Rechner über freie Kapazitäten verfügt, | ||
| 17 | -- z.B. mit dem Tool "htop". Falls der Rechner beschäftigt ist, | ||
| 18 | -- dann loggen Sie sich von dem Remote-Rechner weiter zu einem | ||
| 19 | -- der anderen intern erreichbaren CIP-Arbeitsplatzrechner ein, siehe: | ||
| 20 | -- http://www.rz.ifi.lmu.de/FAQ/Rechnerliste.faq.html | ||
| 21 | -- | ||
| 22 | -- Es ist für diese Übung zwingend notwendig, einen Rechner | ||
| 23 | -- mit mehreren Prozessorkernen einzusetzen. Schließlich dreht es | ||
| 24 | -- sich darum, eine Berechnung durch die gleichzeitge Verwendung | ||
| 25 | -- mehrerer Kerne zu beschleunigen. | ||
| 26 | -- | ||
| 27 | -- Für die nächste Übung 8 zum Thema Nebenläufigkeit reicht | ||
| 28 | -- dann auch wieder ein einzelner Prozessorkern. | ||
| 29 | -- | ||
| 30 | -- | ||
| 31 | -- Anweisungen: | ||
| 32 | -- Es empfiehlt sich heute, die Übungen in einer zur Vorlesung | ||
| 33 | -- umgekehrten Reihenfolge zu bearbeiten, da die Par-Monade | ||
| 34 | -- vermutlich das am einfachsten einzusetzende Konzept ist. | ||
| 35 | -- Für die Vorlesung wurde eine andere Reihenfolge gewählt, | ||
| 36 | -- um die einzelnen Konzepte besser zu motivieren. | ||
| 37 | -- | ||
| 38 | -- Gehen Sie diese Datei durch und bearbeiten Sie | ||
| 39 | -- alle Vorkommen von undefined bzw. die mit -- !!! TODO !!! | ||
| 40 | -- markierten Stellen. Testen Sie Ihre Lösungen mit GHC! | ||
| 41 | -- | ||
| 42 | -- Die Verwendung mehrerer Kerne läßt sich mit dem Interpreter | ||
| 43 | -- (ghci) nur schlecht messen. Kompilieren Sie Ihre Lösung daher | ||
| 44 | -- mit ghc, ggf. jede Aufgabe in eine eigene Datei auslagern: | ||
| 45 | -- > ghc MyProgA71.hs -O2 -threaded -rtsopts | ||
| 46 | -- | ||
| 47 | -- und dann mit gewünschter Anzahl echter Threads ausführen: | ||
| 48 | -- > time ./MyProgA71 +RTS -N4 -s | ||
| 49 | -- > time ./MyProgA71 +RTS -N3 -s | ||
| 50 | -- > time ./MyProgA71 +RTS -N2 -s | ||
| 51 | -- > time ./MyProgA71 +RTS -N1 -s | ||
| 52 | -- die Angabe unter "Total -> elapsed" ist dann ein brauchbares Maß. | ||
| 53 | -- | ||
| 54 | |||
| 55 | -- _________________________________________________ | ||
| 56 | -- | | | ||
| 57 | -- | * Par-Monade kennenlernen | | ||
| 58 | -- | * Parallel Strategies kennenlernen | | ||
| 59 | -- | * seq / deepseq kennenlernen | | ||
| 60 | -- |_________________________________________________| | ||
| 61 | -- λ/ | ||
| 62 | -- /| | ||
| 63 | -- / \ | ||
| 64 | |||
| 65 | import Data.List | ||
| 66 | import Control.Monad | ||
| 67 | import Control.Monad.Par -- ggf. cabal oder "stack install monad-par" ausführen | ||
| 68 | import Control.Parallel | ||
| 69 | import Control.Parallel.Strategies | ||
| 70 | import Control.DeepSeq | ||
| 71 | |||
| 72 | import System.Environment (getArgs) | ||
| 73 | |||
| 74 | -- !!! TODO !!! Je nach bearbeiteter Aufgabe auskommentieren: | ||
| 75 | main = main' [ ("1", main_A7_1) | ||
| 76 | , ("2", main_A7_2) | ||
| 77 | , ("3", main_A7_3) | ||
| 78 | ] | ||
| 79 | |||
| 80 | main' calls = do | ||
| 81 | args <- getArgs | ||
| 82 | mapM_ (\(s, x) -> when (s `elem` args) x) calls | ||
| 83 | |||
| 84 | -- A7-1 Par-Monade | ||
| 85 | -- | ||
| 86 | -- Beschleunigen Sie die Ausführung des folgenden Programmes | ||
| 87 | -- durch den Einsatz einer Par-Monade zur Berechnung. | ||
| 88 | -- | ||
| 89 | -- Hinweis: Sie sollen nur innerhalb der main-Funktion arbeiten. | ||
| 90 | -- Die Funktion hanoi dient nur als Last, also diese selbst nicht beschleunigen. | ||
| 91 | |||
| 92 | main_A7_1 :: IO () | ||
| 93 | main_A7_1 = main' [ ("seq", main_A7_1seq) | ||
| 94 | , ("par", main_A7_1par) | ||
| 95 | ] | ||
| 96 | |||
| 97 | main_A7_1seq :: IO () | ||
| 98 | main_A7_1seq = do | ||
| 99 | let difficulty = 25 -- ändern, falls Laufzeit zu lang oder zu kurz ist | ||
| 100 | t1 = length $ hanoi difficulty 1 2 | ||
| 101 | t2 = length $ hanoi difficulty 1 3 | ||
| 102 | t3 = length $ hanoi difficulty 2 3 | ||
| 103 | t4 = length $ hanoi difficulty 3 1 | ||
| 104 | t1 `par` t2 `par` t3 `par` t4 `pseq` putStrLn "Wusstest Du schon:" | ||
| 105 | putStrLn $ "Man braucht " ++ (show t1) ++ " Schritte um einen Hanoi-Turm der Größe " | ||
| 106 | ++ (show difficulty) ++ " von Platz 1 auf Platz 2 zu versetzen." | ||
| 107 | putStrLn $ "Man braucht " ++ (show t2) ++ " Schritte um einen Hanoi-Turm der Größe " | ||
| 108 | ++ (show difficulty) ++ " von Platz 1 auf Platz 3 zu versetzen." | ||
| 109 | putStrLn $ "Man braucht " ++ (show t3) ++ " Schritte um einen Hanoi-Turm der Größe " | ||
| 110 | ++ (show difficulty) ++ " von Platz 2 auf Platz 3 zu versetzen." | ||
| 111 | putStrLn $ "Man braucht " ++ (show t4) ++ " Schritte um einen Hanoi-Turm der Größe " | ||
| 112 | ++ (show difficulty) ++ " von Platz 3 auf Platz 1 zu versetzen." | ||
| 113 | putStrLn "Gut zu wissen, oder?" | ||
| 114 | |||
| 115 | |||
| 116 | main_A7_1par :: IO () -- parallele Version | ||
| 117 | main_A7_1par = undefined -- !!! TODO !!! | ||
| 118 | |||
| 119 | |||
| 120 | -- Hilfsfunktionen, zur Erzeugung von Rechnenlast, bitte nicht verändern: | ||
| 121 | hanoi :: Int -> Int -> Int -> [(Int,Int)] | ||
| 122 | hanoi 1 i j = [(i,j)] | ||
| 123 | hanoi n i j = hanoi n' i otherTower | ||
| 124 | ++ [(i,j)] | ||
| 125 | ++ hanoi n' otherTower j | ||
| 126 | where | ||
| 127 | n' = n-1 | ||
| 128 | otherTower = 1+2+3-i-j | ||
| 129 | |||
| 130 | |||
| 131 | |||
| 132 | -- A7-2 Parallel.Strategies | ||
| 133 | -- | ||
| 134 | -- In der Vorlesung wurden parallele Strategien vorgestellt. | ||
| 135 | -- Implementieren Sie ein parallel-beschleunigtes Quicksort unter Verwendung von | ||
| 136 | -- Strategien aus Control.Parallel.Strategies! | ||
| 137 | -- | ||
| 138 | -- Testen und vergleichen Sie verschiedene Auswerte-Strategien! | ||
| 139 | -- Welche ist die Beste und warum? | ||
| 140 | -- | ||
| 141 | -- Hinweis: | ||
| 142 | -- Die Laufzeit mit einem Kern sollte im CIP-Pool ca. 1 Minuten dauern. | ||
| 143 | |||
| 144 | quicksortS :: [Integer] -> [Integer] | ||
| 145 | quicksortS [] = [] | ||
| 146 | quicksortS (x:xs) = result | ||
| 147 | where | ||
| 148 | result = losort ++ (x:hisort) | ||
| 149 | losort = quicksortS [y | y <- xs, y < x] | ||
| 150 | hisort = quicksortS [y | y <- xs, y >= x] | ||
| 151 | |||
| 152 | quicksortP :: [Integer] -> [Integer] | ||
| 153 | quicksortP xs = undefined -- !!! TODO !!! | ||
| 154 | |||
| 155 | |||
| 156 | main_A7_2 = do | ||
| 157 | args <- getArgs | ||
| 158 | let qs | ||
| 159 | | "seq" `elem` args = quicksortS | ||
| 160 | | otherwise = quicksortP | ||
| 161 | putStrLn "Berechnung von Quicksort gestartet." | ||
| 162 | let list1 = [1,7..30000] ++ (reverse [1..45678]) ++ [2,4..30000] | ||
| 163 | let qlist1 = qs list1 | ||
| 164 | let len1 = deepseq qlist1 (length list1) | ||
| 165 | let list2 = concat $ permutations $ [1..7] | ||
| 166 | let qlist2 = qs list2 | ||
| 167 | let len2 = deepseq qlist2 (length list2) | ||
| 168 | putStrLn $ "Berechnung von Quicksort mit Liste der Länge " ++ (show len1) ++ ":" | ||
| 169 | -- putStrLn $ show $ take 33 $ drop 22222 $ quicksortS list1 | ||
| 170 | putStrLn $ "Berechnung von Quicksort mit Liste der Länge " ++ (show len2) ++ ":" | ||
| 171 | -- putStrLn $ show $ take 33 $ drop 22222 $ quicksortS list2 | ||
| 172 | putStrLn $ "Done." | ||
| 173 | |||
| 174 | |||
| 175 | |||
| 176 | -- A7-3 par und pseq | ||
| 177 | -- | ||
| 178 | -- Betrachten Sie die Funktionen pay und payL, welche für einen gegebenen Betrag | ||
| 179 | -- und einer gegebenen Menge Münzen [(Münzgröße,Anzahl)] alle Möglichkeiten | ||
| 180 | -- ausgibt, diesen Betrag mit den Münzen zu bezahlen: | ||
| 181 | -- pay 10 [(3,3),(5,2),(1,2)] = [[(5,2)],[(1,2),(3,1),(5,1)],[(1,1),(3,3)]] | ||
| 182 | -- | ||
| 183 | -- Beschleunigen Sie die Ausführung der Funktion pay bzw. payL | ||
| 184 | -- durch den Einsatz von par und pseq | ||
| 185 | -- | ||
| 186 | type Münzgröße = Integer | ||
| 187 | type Anzahl = Integer | ||
| 188 | type Betrag = Integer | ||
| 189 | |||
| 190 | main_A7_3 = do | ||
| 191 | let value = 2345 | ||
| 192 | let coins = [(23,25),(11,25),(33,50),(5,25),(3,25),(1,25)] | ||
| 193 | putStrLn $ "Berechne die Möglichkeiten den Wert " ++ (show value) ++ " mit den Münzen & Scheinen " ++ (show coins) ++ " darzustellen:" | ||
| 194 | let poss = pay value coins | ||
| 195 | -- mapM_ print $ poss | ||
| 196 | putStrLn $ "Es gibt " ++ (show $ length poss) ++ " Möglichkeiten den Wert " ++ (show value) ++ " mit den Münzen & Scheinen " ++ (show coins) ++ " darzustellen!" | ||
| 197 | |||
| 198 | pay :: Betrag -> [(Münzgröße, Anzahl)] -> [[(Münzgröße, Anzahl)]] | ||
| 199 | pay val coins = map count $ payL [] val (sortBy (\(a,_) (c,_) -> invert $ compare a c) coins) | ||
| 200 | |||
| 201 | payL :: [Integer] -> Betrag -> [(Münzgröße,Anzahl)] -> [[Integer]] | ||
| 202 | payL acc 0 coins = [acc] | ||
| 203 | payL acc _ [] = [] | ||
| 204 | payL acc val ((c,qty):coins) | ||
| 205 | | c > val = payL acc val coins | ||
| 206 | | otherwise = left ++ right | ||
| 207 | where | ||
| 208 | left = payL (c:acc) (val - c) coins' | ||
| 209 | right = payL acc val coins | ||
| 210 | coins' | qty == 1 = coins | ||
| 211 | | otherwise = (c,qty-1) : coins | ||
| 212 | |||
| 213 | count :: [Integer] -> [(Integer,Integer)] | ||
| 214 | count xs = [(head ks, genericLength ks) | ks <- group $ sort xs] | ||
| 215 | |||
| 216 | invert :: Ordering -> Ordering | ||
| 217 | invert EQ = EQ | ||
| 218 | invert LT = GT | ||
| 219 | invert GT = LT | ||
