diff options
Diffstat (limited to 'ws2015')
-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 | ||