git.iko.soy/distributed-process
on master help sign in
Merge branch 'ring-benchmarks' into development
commitc77c172782dc71c233ff2ac57acf434257b7b4ad authorTim Watson <tim@rabbitmq.com> dateWed 24 Apr 2013, 13:09 (13 years ago) parent06b3290f2ec0d8
3 files changed+189
benchmarks/ProcessRing.hs+104 benchmarks/erlang/ring.erl+62 distributed-process.cabal+23
benchmarks/ProcessRing.hsnew file+104
@@ -0,0 +1,104 @@
1+{- ProcessRing benchmarks.
2+
3+To run the benchmarks, select a value for the ring size (sz) and
4+the number of times to send a message around the ring
5+-}
6+import Control.Monad
7+import Control.Distributed.Process hiding (catch)
8+import Control.Distributed.Process.Node
9+import Network.Transport.TCP (createTransport, defaultTCPParameters)
10+import System.Environment
11+import System.Console.GetOpt
12+
13+data Options = Options
14+ { optRingSize :: Int
15+ , optIterations :: Int
16+ , optForward :: Bool
17+ , optParallel :: Bool
18+ } deriving Show
19+
20+initialProcess :: Options -> Process ()
21+initialProcess op =
22+ let ringSz = optRingSize op
23+ msgCnt = optIterations op
24+ fwd = optForward op
25+ msg = ("foobar", "baz")
26+ in do
27+ self <- getSelfPid
28+ ring <- makeRing fwd ringSz self
29+ forM_ [1..msgCnt] (\_ -> send ring msg)
30+ collect msgCnt
31+ where relay' pid = do
32+ msg <- expect :: Process (String, String)
33+ send pid msg
34+ relay' pid
35+
36+ forward' pid =
37+ receiveWait [ matchAny (\m -> forward m pid) ] >> forward' pid
38+
39+ makeRing :: Bool -> Int -> ProcessId -> Process ProcessId
40+ makeRing !f !n !pid
41+ | n == 0 = go f pid
42+ | otherwise = go f pid >>= makeRing f (n - 1)
43+
44+ go :: Bool -> ProcessId -> Process ProcessId
45+ go False next = spawnLocal $ relay' next
46+ go True next = spawnLocal $ forward' next
47+
48+ collect :: Int -> Process ()
49+ collect !n
50+ | n == 0 = return ()
51+ | otherwise = do
52+ receiveWait [
53+ matchIf (\(a, b) -> a == "foobar" && b == "baz")
54+ (\_ -> return ())
55+ , matchAny (\_ -> error "unexpected input!")
56+ ]
57+ collect (n - 1)
58+
59+defaultOptions :: Options
60+defaultOptions = Options
61+ { optRingSize = 10
62+ , optIterations = 100
63+ , optForward = False
64+ , optParallel = False
65+ }
66+
67+options :: [OptDescr (Options -> Options)]
68+options =
69+ [ Option ['s'] ["ring-size"] (OptArg optSz "SIZE") "# of processes in ring"
70+ , Option ['i'] ["iterations"] (OptArg optMsgCnt "ITER") "# of times to send"
71+ , Option ['f'] ["forward"]
72+ (NoArg (\opts -> opts { optForward = True }))
73+ "use `forward' instead of send - default = False"
74+ , Option ['p'] ["parallel"]
75+ (NoArg (\opts -> opts { optForward = True }))
76+ "send in parallel and consume sequentially - default = False"
77+ ]
78+
79+optMsgCnt :: Maybe String -> Options -> Options
80+optMsgCnt Nothing opts = opts
81+optMsgCnt (Just c) opts = opts { optIterations = ((read c) :: Int) }
82+
83+optSz :: Maybe String -> Options -> Options
84+optSz Nothing opts = opts
85+optSz (Just s) opts = opts { optRingSize = ((read s) :: Int) }
86+
87+parseArgv :: [String] -> IO (Options, [String])
88+parseArgv argv = do
89+ pn <- getProgName
90+ case getOpt Permute options argv of
91+ (o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)
92+ (_,_,errs) -> ioError (userError (concat errs ++ usageInfo (header pn) options))
93+ where header pn' = "Usage: " ++ pn' ++ " [OPTION...]"
94+
95+main :: IO ()
96+main = do
97+ argv <- getArgs
98+ (opt, _) <- parseArgv argv
99+ putStrLn $ "options: " ++ (show opt)
100+ Right transport <- createTransport "127.0.0.1" "8090" defaultTCPParameters
101+ node <- newLocalNode transport initRemoteTable
102+ catch (runProcess node $ initialProcess opt)
103+ (\e -> putStrLn $ "ERROR: " ++ (show e))
104+
benchmarks/erlang/ring.erlnew file+62
@@ -0,0 +1,62 @@
1+%% can be run via escript if desired...
2+-module(ring).
3+
4+-export([main/1]).
5+
6+-record(opts, {
7+ ring_size = 1 :: non_neg_integer(),
8+ iterations = 1 :: non_neg_integer()
9+}).
10+
11+main(Argv) when is_list(Argv) ->
12+ Opts = parse_args(Argv),
13+ {Time, ok} = timer:tc(fun() -> run(Opts) end),
14+ io:format("elapsed (wallclock) time: ~pms~n", [Time / 1000]).
15+
16+run(#opts{ ring_size=RingSize, iterations=SendCount }) ->
17+ Self = self(),
18+ Msg = {"foobar", "baz"},
19+ Pid = make_ring(RingSize, Self),
20+ [Pid ! Msg || _ <- lists:seq(1, SendCount)],
21+ collect(SendCount).
22+
23+collect(0) ->
24+ ok;
25+collect(N) ->
26+ receive
27+ {"foobar", "baz"} -> collect(N - 1);
28+ Other -> exit({unexpected_message, Other})
29+ end.
30+
31+-spec(make_ring(integer(), pid()) -> pid()).
32+make_ring(0, NextPid) ->
33+ go(NextPid);
34+make_ring(NumProcs, NextPid) ->
35+ make_ring(NumProcs - 1, go(NextPid)).
36+
37+-spec(go(pid()) -> pid()).
38+go(NextPid) ->
39+ spawn(fun() -> relay(NextPid) end).
40+
41+-spec(relay(pid()) -> no_return()).
42+relay(NextPid) ->
43+ receive
44+ {_, _}=Msg -> NextPid ! Msg, relay(NextPid)
45+ end.
46+
47+-spec(parse_args([string()]) -> #opts{}).
48+parse_args(Argv) ->
49+ lists:foldl(fun parse_args/2, #opts{}, Argv).
50+
51+parse_args(("-r" ++ Val), Opts) ->
52+ Sz = check_positive(ring_size, list_to_integer(Val)),
53+ Opts#opts{ ring_size = Sz };
54+parse_args(("-i" ++ Val), Opts) ->
55+ Iter = check_positive(iterations, list_to_integer(Val)),
56+ Opts#opts{ iterations = Iter }.
57+
58+check_positive(K, N) when N < 1 ->
59+ io:format("~p must be >= 1~n", [K]),
60+ error({K, N});
61+check_positive(_, N) ->
62+ N.
distributed-process.cabal+23
@@ -37,6 +37,10 @@ flag benchmarks
3737 description: Build benchmarks
3838 default: False
3939
40+flag prof
41+ description: Compiling with profiling enabled
42+ default: False
43+
4044Library
4145 Build-Depends: base >= 4.4 && < 5,
4246 binary >= 0.5 && < 0.7,
@@ -201,3 +205,22 @@ Executable distributed-process-spawns
201205 Main-Is: benchmarks/Spawns.hs
202206 ghc-options: -Wall
203207 Extensions: BangPatterns
208+
209+Executable distributed-process-ring
210+ if flag(benchmarks)
211+ Build-Depends: base >= 4.4 && < 5,
212+ distributed-process,
213+ network-transport-tcp >= 0.3 && < 0.4,
214+ bytestring >= 0.9 && < 0.11,
215+ binary >= 0.5 && < 0.7
216+ else
217+ buildable: False
218+ Main-Is: benchmarks/ProcessRing.hs
219+ if flag(prof)
220+ ghc-options: -Wall -threaded -fprof-auto -fno-prof-count-entries
221+ else
222+ ghc-options: -Wall -threaded -O2 -H256m -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
223+ Extensions: BangPatterns,
224+ ScopedTypeVariables
225+
226+