git.iko.soy/distributed-process
on master help sign in
Monomorphic implementation of map-reduce
commit523bc57f5bc7a0a4c95479eca306e6e60b33b1d2 authorEdsko de Vries <edsko@edsko.net> dateWed 3 Oct 2012, 10:22 (14 years ago) parent9ae769c
6 files changed+198−136
distributed-process-demos/src/MapReduce/CountWords.hs+5−14 distributed-process-demos/src/MapReduce/KMeans.hs+2−1 distributed-process-demos/src/MapReduce/MapReduce.hs+4−119 distributed-process-demos/src/MapReduce/MonoDistrMapReduce.hs+60 distributed-process-demos/src/MapReduce/PolyDistrMapReduce.hs+123 distributed-process-demos/src/MapReduce/SimpleLocalnet.hs+4−2
distributed-process-demos/src/MapReduce/CountWords.hs+5−14
@@ -8,7 +8,8 @@ module CountWords
88
99import Control.Distributed.Process
1010import Control.Distributed.Process.Closure
11−import MapReduce hiding (__remoteTable)
11+import MapReduce
12+import MonoDistrMapReduce hiding (__remoteTable)
1213
1314type Document = String
1415type Word = String
@@ -23,21 +24,11 @@ countWords = MapReduce {
2324localCountWords :: Map FilePath Document -> Map Word Frequency
2425localCountWords = localMapReduce countWords
2526
26−dictIn :: SerializableDict (FilePath, Document)
27−dictIn = SerializableDict
28−
29−dictOut :: SerializableDict [(Word, Frequency)]
30−dictOut = SerializableDict
31−
3227countWords_ :: () -> MapReduce FilePath Document Word Frequency Frequency
3328countWords_ () = countWords
3429
35−remotable ['dictIn, 'dictOut, 'countWords_]
30+remotable ['countWords_]
3631
3732distrCountWords :: [NodeId] -> Map FilePath Document -> Process (Map Word Frequency)
38−distrCountWords mappers input =
39− distrMapReduce $(mkStatic 'dictIn)
40− $(mkStatic 'dictOut)
41− ($(mkClosure 'countWords_) ())
42− mappers
43− (\iteration -> iteration input)
33+distrCountWords = distrMapReduce ($(mkClosure 'countWords_) ())
34+
distributed-process-demos/src/MapReduce/KMeans.hs+2−1
@@ -15,7 +15,8 @@ import Data.Array (Array, (!), bounds)
1515import qualified Data.Map as Map (fromList, elems, toList, size)
1616import Control.Distributed.Process
1717import Control.Distributed.Process.Closure
18−import MapReduce hiding (__remoteTable)
18+import MapReduce
19+import PolyDistrMapReduce hiding (__remoteTable)
1920
2021type Point = (Double, Double)
2122type Cluster = (Double, Double)
distributed-process-demos/src/MapReduce/MapReduce.hs+4−119
@@ -3,23 +3,17 @@ module MapReduce
33 ( -- * Map-reduce skeleton and implementation
44 MapReduce(..)
55 , localMapReduce
6− , distrMapReduce
7− , __remoteTable
6+ -- * Map-reduce algorithmic components
7+ , reducePerKey
8+ , groupByKey
89 -- * Re-exports from Data.Map
910 , Map
1011 ) where
1112
1213import Data.Typeable (Typeable)
13−import Data.Binary (encode)
14−import Data.ByteString.Lazy (ByteString)
1514import Data.Map (Map)
16−import qualified Data.Map as Map (mapWithKey, fromListWith, toList, size)
15+import qualified Data.Map as Map (mapWithKey, fromListWith, toList)
1716import Control.Arrow (second)
18−import Control.Monad (forM_, replicateM, replicateM_)
19−import Control.Distributed.Process
20−import Control.Distributed.Process.Serializable (Serializable)
21−import Control.Distributed.Process.Closure
22−import Control.Distributed.Static (closureApply, staticCompose, staticApply)
2317
2418--------------------------------------------------------------------------------
2519-- Definition of a MapReduce skeleton and a local implementation --
@@ -50,112 +44,3 @@ groupByKey = Map.fromListWith (++) . map (second return)
5044
5145mapPerKey :: MapReduce k1 v1 k2 v2 v3 -> Map k1 v1 -> [(k2, v2)]
5246mapPerKey mr = concatMap (uncurry (mrMap mr)) . Map.toList
53−
54−--------------------------------------------------------------------------------
55−-- Simple distributed implementation --
56−--------------------------------------------------------------------------------
57−
58−matchDict :: forall a b. SerializableDict a -> (a -> Process b) -> Match b
59−matchDict SerializableDict = match
60−
61−sendDict :: forall a. SerializableDict a -> ProcessId -> a -> Process ()
62−sendDict SerializableDict = send
63−
64−sdictProcessIdPair :: SerializableDict (ProcessId, ProcessId)
65−sdictProcessIdPair = SerializableDict
66−
67−mapperProcess :: forall k1 v1 k2 v2 v3.
68− SerializableDict (k1, v1)
69− -> SerializableDict [(k2, v2)]
70− -> (ProcessId, ProcessId)
71− -> MapReduce k1 v1 k2 v2 v3
72− -> Process ()
73−mapperProcess dictIn dictOut (master, workQueue) mr = getSelfPid >>= go
74− where
75− go us = do
76− -- Ask the queue for work
77− send workQueue us
78−
79− -- Wait for a reply; if there is work, do it and repeat; otherwise, exit
80− receiveWait
81− [ matchDict dictIn $ \(key, val) -> do
82− sendDict dictOut master (mrMap mr key val)
83− go us
84− , match $ \() ->
85− return ()
86− ]
87−
88−remotable ['mapperProcess, 'sdictProcessIdPair]
89−
90−mapperProcessClosure :: forall k1 v1 k2 v2 v3.
91− (Typeable k1, Typeable v1, Typeable k2, Typeable v2, Typeable v3)
92− => Static (SerializableDict (k1, v1))
93− -> Static (SerializableDict [(k2, v2)])
94− -> Closure (MapReduce k1 v1 k2 v2 v3)
95− -> ProcessId
96− -> ProcessId
97− -> Closure (Process ())
98−mapperProcessClosure dictIn dictOut mr master workQueue =
99− closure decoder (encode (master, workQueue)) `closureApply` mr
100− where
101− decoder :: Static (ByteString -> MapReduce k1 v1 k2 v2 v3 -> Process ())
102− decoder =
103− ($(mkStatic 'mapperProcess) `staticApply` dictIn `staticApply` dictOut)
104− `staticCompose`
105− staticDecode $(mkStatic 'sdictProcessIdPair)
106−
107−distrMapReduce :: forall k1 k2 v1 v2 v3 a.
108− (Serializable k1, Serializable v1, Serializable k2, Serializable v2, Serializable v3, Ord k2)
109− => Static (SerializableDict (k1, v1))
110− -> Static (SerializableDict [(k2, v2)])
111− -> Closure (MapReduce k1 v1 k2 v2 v3)
112− -> [NodeId]
113− -> ((Map k1 v1 -> Process (Map k2 v3)) -> Process a)
114− -> Process a
115−distrMapReduce dictIn dictOut mr mappers p = do
116− mr' <- unClosure mr
117− master <- getSelfPid
118−
119− workQueue <- spawnChannelLocal $ \queue -> do
120− let go :: Process ()
121− go = do
122− mWork <- receiveChan queue
123− case mWork of
124− Just (key, val) -> do
125− -- As long there is work, make it available to the mappers
126− them <- expect
127− send them (key, val)
128− go
129− Nothing -> do
130− -- Tell the mappers to terminate
131− replicateM_ (length mappers) $ do
132− them <- expect
133− send them ()
134−
135− -- Tell the master that the slaves are terminated
136− send master ()
137− go
138−
139− -- Start the mappers
140− let workQueuePid = sendPortProcessId (sendPortId workQueue)
141− forM_ mappers $ \nid ->
142− spawn nid (mapperProcessClosure dictIn dictOut mr master workQueuePid)
143−
144− let iteration :: Map k1 v1 -> Process (Map k2 v3)
145− iteration input = do
146− -- Make work available to the mappers
147− mapM_ (sendChan workQueue . Just) (Map.toList input)
148−
149− -- Wait for the partial results
150− partials <- replicateM (Map.size input) expect
151−
152− -- We reduce on this node
153− return (reducePerKey mr' . groupByKey . concat $ partials)
154−
155− result <- p iteration
156−
157− -- Terminate the wrappers
158− sendChan workQueue Nothing
159− expect :: Process ()
160−
161− return result
distributed-process-demos/src/MapReduce/MonoDistrMapReduce.hsnew file+60
@@ -0,0 +1,60 @@
1+-- | Monomorphic "single-shot" distributed implementation of map-reduce
2+module MonoDistrMapReduce (distrMapReduce, __remoteTable) where
3+
4+import Data.Map (Map)
5+import qualified Data.Map as Map (size, toList)
6+import Control.Monad (forM_, replicateM, replicateM_)
7+import Control.Distributed.Process
8+import Control.Distributed.Process.Closure
9+import MapReduce (MapReduce(..), reducePerKey, groupByKey)
10+
11+--------------------------------------------------------------------------------
12+-- Simple distributed implementation --
13+--------------------------------------------------------------------------------
14+
15+mapperProcess :: (ProcessId, ProcessId, Closure (MapReduce String String String Int Int))
16+ -> Process ()
17+mapperProcess (master, workQueue, mrClosure) = do
18+ us <- getSelfPid
19+ mr <- unClosure mrClosure
20+ go us mr
21+ where
22+ go us mr = do
23+ -- Ask the queue for work
24+ send workQueue us
25+
26+ -- Wait for a reply; if there is work, do it and repeat; otherwise, exit
27+ receiveWait
28+ [ match $ \(key, val) -> send master (mrMap mr key val) >> go us mr
29+ , match $ \() -> return ()
30+ ]
31+
32+remotable ['mapperProcess]
33+
34+distrMapReduce :: Closure (MapReduce String String String Int Int)
35+ -> [NodeId]
36+ -> Map String String
37+ -> Process (Map String Int)
38+distrMapReduce mrClosure mappers input = do
39+ mr <- unClosure mrClosure
40+ master <- getSelfPid
41+
42+ workQueue <- spawnLocal $ do
43+ -- Return the next bit of work to be done
44+ forM_ (Map.toList input) $ \(key, val) -> do
45+ them <- expect
46+ send them (key, val)
47+
48+ -- Once all teh work is done tell the mappers to terminate
49+ replicateM_ (length mappers) $ do
50+ them <- expect
51+ send them ()
52+
53+ -- Start the mappers
54+ forM_ mappers $ \nid -> spawn nid ($(mkClosure 'mapperProcess) (master, workQueue, mrClosure))
55+
56+ -- Wait for the partial results
57+ partials <- replicateM (Map.size input) expect
58+
59+ -- We reduce on this node
60+ return (reducePerKey mr . groupByKey . concat $ partials)
distributed-process-demos/src/MapReduce/PolyDistrMapReduce.hsnew file+123
@@ -0,0 +1,123 @@
1+-- | Polymorphic "multi-shot" distributed implementation of map-reduce
2+module PolyDistrMapReduce (distrMapReduce, __remoteTable) where
3+
4+import Data.Typeable (Typeable)
5+import Data.Binary (encode)
6+import Data.ByteString.Lazy (ByteString)
7+import Data.Map (Map)
8+import qualified Data.Map as Map (size, toList)
9+import Control.Monad (forM_, replicateM, replicateM_)
10+import Control.Distributed.Process
11+import Control.Distributed.Process.Serializable (Serializable)
12+import Control.Distributed.Process.Closure
13+import Control.Distributed.Static (closureApply, staticCompose, staticApply)
14+import MapReduce (MapReduce(..), reducePerKey, groupByKey)
15+
16+--------------------------------------------------------------------------------
17+-- Simple distributed implementation --
18+--------------------------------------------------------------------------------
19+
20+matchDict :: forall a b. SerializableDict a -> (a -> Process b) -> Match b
21+matchDict SerializableDict = match
22+
23+sendDict :: forall a. SerializableDict a -> ProcessId -> a -> Process ()
24+sendDict SerializableDict = send
25+
26+sdictProcessIdPair :: SerializableDict (ProcessId, ProcessId)
27+sdictProcessIdPair = SerializableDict
28+
29+mapperProcess :: forall k1 v1 k2 v2 v3.
30+ SerializableDict (k1, v1)
31+ -> SerializableDict [(k2, v2)]
32+ -> (ProcessId, ProcessId)
33+ -> MapReduce k1 v1 k2 v2 v3
34+ -> Process ()
35+mapperProcess dictIn dictOut (master, workQueue) mr = getSelfPid >>= go
36+ where
37+ go us = do
38+ -- Ask the queue for work
39+ send workQueue us
40+
41+ -- Wait for a reply; if there is work, do it and repeat; otherwise, exit
42+ receiveWait
43+ [ matchDict dictIn $ \(key, val) -> do
44+ sendDict dictOut master (mrMap mr key val)
45+ go us
46+ , match $ \() ->
47+ return ()
48+ ]
49+
50+remotable ['mapperProcess, 'sdictProcessIdPair]
51+
52+mapperProcessClosure :: forall k1 v1 k2 v2 v3.
53+ (Typeable k1, Typeable v1, Typeable k2, Typeable v2, Typeable v3)
54+ => Static (SerializableDict (k1, v1))
55+ -> Static (SerializableDict [(k2, v2)])
56+ -> Closure (MapReduce k1 v1 k2 v2 v3)
57+ -> ProcessId
58+ -> ProcessId
59+ -> Closure (Process ())
60+mapperProcessClosure dictIn dictOut mr master workQueue =
61+ closure decoder (encode (master, workQueue)) `closureApply` mr
62+ where
63+ decoder :: Static (ByteString -> MapReduce k1 v1 k2 v2 v3 -> Process ())
64+ decoder =
65+ ($(mkStatic 'mapperProcess) `staticApply` dictIn `staticApply` dictOut)
66+ `staticCompose`
67+ staticDecode $(mkStatic 'sdictProcessIdPair)
68+
69+distrMapReduce :: forall k1 k2 v1 v2 v3 a.
70+ (Serializable k1, Serializable v1, Serializable k2, Serializable v2, Serializable v3, Ord k2)
71+ => Static (SerializableDict (k1, v1))
72+ -> Static (SerializableDict [(k2, v2)])
73+ -> Closure (MapReduce k1 v1 k2 v2 v3)
74+ -> [NodeId]
75+ -> ((Map k1 v1 -> Process (Map k2 v3)) -> Process a)
76+ -> Process a
77+distrMapReduce dictIn dictOut mr mappers p = do
78+ mr' <- unClosure mr
79+ master <- getSelfPid
80+
81+ workQueue <- spawnChannelLocal $ \queue -> do
82+ let go :: Process ()
83+ go = do
84+ mWork <- receiveChan queue
85+ case mWork of
86+ Just (key, val) -> do
87+ -- As long there is work, make it available to the mappers
88+ them <- expect
89+ send them (key, val)
90+ go
91+ Nothing -> do
92+ -- Tell the mappers to terminate
93+ replicateM_ (length mappers) $ do
94+ them <- expect
95+ send them ()
96+
97+ -- Tell the master that the slaves are terminated
98+ send master ()
99+ go
100+
101+ -- Start the mappers
102+ let workQueuePid = sendPortProcessId (sendPortId workQueue)
103+ forM_ mappers $ \nid ->
104+ spawn nid (mapperProcessClosure dictIn dictOut mr master workQueuePid)
105+
106+ let iteration :: Map k1 v1 -> Process (Map k2 v3)
107+ iteration input = do
108+ -- Make work available to the mappers
109+ mapM_ (sendChan workQueue . Just) (Map.toList input)
110+
111+ -- Wait for the partial results
112+ partials <- replicateM (Map.size input) expect
113+
114+ -- We reduce on this node
115+ return (reducePerKey mr' . groupByKey . concat $ partials)
116+
117+ result <- p iteration
118+
119+ -- Terminate the wrappers
120+ sendChan workQueue Nothing
121+ expect :: Process ()
122+
123+ return result
distributed-process-demos/src/MapReduce/SimpleLocalnet.hs+4−2
@@ -11,11 +11,13 @@ import Data.Array (Array, listArray)
1111import qualified Data.Map as Map (fromList)
1212
1313import qualified CountWords
14−import qualified MapReduce
14+import qualified PolyDistrMapReduce
15+import qualified MonoDistrMapReduce
1516import qualified KMeans
1617
1718rtable :: RemoteTable
18−rtable = MapReduce.__remoteTable
19+rtable = PolyDistrMapReduce.__remoteTable
20+ . MonoDistrMapReduce.__remoteTable
1921 . CountWords.__remoteTable
2022 . KMeans.__remoteTable
2123 $ initRemoteTable