@@ -1,4 +1,4 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE CPP, BangPatterns #-}
3module Main where
3module Main where
@@ -6,14 +6,14 @@ import Network.Transport (newConnection, receive, connect, send, defaultHints,
6 serialize, deserialize, SourceEnd, TargetEnd)
6 serialize, deserialize, SourceEnd, TargetEnd)
7import Network.Transport.TCP (mkTransport, TCPConfig (..))
7import Network.Transport.TCP (mkTransport, TCPConfig (..))
9−import Control.Monad (forever, replicateM_)
9+import Control.Monad (forever, replicateM_, forM_)
10−import Criterion.Main (Benchmark, bench, defaultMainWith, nfIO)
11−import Criterion.Config (defaultConfig, ljust, Config(cfgSamples))
13import qualified Data.Serialize as Ser
11import qualified Data.Serialize as Ser
14import Data.Maybe (fromJust)
12import Data.Maybe (fromJust)
15import Data.Int
13import Data.Int
16import System.Environment (getArgs, withArgs)
14import System.Environment (getArgs, withArgs)
15+import Data.Time (getCurrentTime, diffUTCTime)
16+import System.IO (withFile, IOMode(..), hPutStrLn)
18#ifndef LAZY
18#ifndef LAZY
19import qualified Data.ByteString.Char8 as BS
19import qualified Data.ByteString.Char8 as BS
@@ -68,10 +68,10 @@ main = do
69 -- always respond to a ping with a pong
69 -- always respond to a ping with a pong
70 putStrLn "server: awaiting pings"
70 putStrLn "server: awaiting pings"
71− forever $ pong targetEndPing sourceEndPong
71+ pong targetEndPing sourceEndPong
74− "client" : host : service : sourceAddrFilePath : pingsStr : reps : args' -> do
74+ "client" : host : service : sourceAddrFilePath : pingsStr : [] -> do
75 let pings = read pingsStr
75 let pings = read pingsStr
76 -- establish transport
76 -- establish transport
77 transport <- mkTransport $ TCPConfig defaultHints host service
77 transport <- mkTransport $ TCPConfig defaultHints host service
@@ -84,39 +84,39 @@ main = do
84 (sourceAddrPong, targetEndPong) <- newConnection transport
84 (sourceAddrPong, targetEndPong) <- newConnection transport
85 send sourceEndPing [serialize sourceAddrPong]
85 send sourceEndPing [serialize sourceAddrPong]
87− -- benchmark the pings
88− case (read reps) :: Int of
89− 0 -> error "What would zero reps mean?"
90− 1 -> do putStrLn "Because you're timing only one trial, skipping Criterion..."
91− replicateM_ pings (ping sourceEndPing targetEndPong 42)
87+ ping sourceEndPing targetEndPong pings
92− n -> withArgs args' $ defaultMainWith
93− (defaultConfig{ cfgSamples = ljust n })
94− (return ()) -- Init action.
95− [ benchPing sourceEndPing targetEndPong (fromIntegral pings)]
96 putStrLn "Done with all ping/pongs."
88 putStrLn "Done with all ping/pongs."
98−-- | This function takes a `TargetEnd` for the pings, and a `SourceEnd` for
90+-- | The message we use to ping (CAF so that we don't keep encoding it)
99−-- pongs. Whenever a ping is received from the `TargetEnd`, a pong is sent
91+pingMessage :: BS.ByteString
100−-- in reply down the `SourceEnd`, repeating whatever was sent.
92+pingMessage = BS.pack "ping123"
94+-- | Keep replying to pings (send pongs)
101pong :: TargetEnd -> SourceEnd -> IO ()
95pong :: TargetEnd -> SourceEnd -> IO ()
102pong targetEndPing sourceEndPong = do
96pong targetEndPing sourceEndPong = do
103 bs <- receive targetEndPing
97 bs <- receive targetEndPing
104 send sourceEndPong bs
98 send sourceEndPong bs
105−
99+ pong targetEndPing sourceEndPong
106−-- | The effect of `ping sourceEndPing targetEndPong n` is to send the number
100+
107−-- `n` using `sourceEndPing`, and to then receive the a number from
101+-- | Send a number of pings
108−-- `targetEndPong`, which is then returned.
102+ping :: SourceEnd -> TargetEnd -> Int -> IO ()
109−ping :: SourceEnd -> TargetEnd -> Int64 -> IO Int64
103+ping sourceEndPing targetEndPong pings = go [] pings
110−ping sourceEndPing targetEndPong n = do
104+ where
105+ go :: [Double] -> Int -> IO ()
108+ putStrLn $ "client did " ++ show pings ++ " pings"
110+ before <- getCurrentTime
111− send sourceEndPing [encode n]
111+ send sourceEndPing [pingMessage]
112− [bs] <- receive targetEndPong
112+ [bs] <- receive targetEndPong
113− let (Right n) = decode bs
113+ after <- getCurrentTime
114− return $! n
114+ let latency = (1e6 :: Double) * realToFrac (diffUTCTime after before)
115−
115+ latency `seq` go (latency : rtl) (i - 1)
116−-- | The effect of `benchPing sourceEndPing targetEndPong n` is to send
116+
117−-- `n` pings down `sourceEndPing` using the `ping` function. The time
117+-- | Output latencies to a file to be plotted
118−-- taken is benchmarked.
118+outputData :: [Double] -> IO ()
119−benchPing :: SourceEnd -> TargetEnd -> Int64 -> Benchmark
119+outputData rtl = do
120−benchPing sourceEndPing targetEndPong n = bench "PingTransport" $
120+ withFile "round-trip-latency-tcp-transport.data" WriteMode $ \h ->
121− nfIO (replicateM_ (fromIntegral n) (ping sourceEndPing targetEndPong 42))
121+ forM_ (zip [0..] rtl) $ \(i, latency) ->
122−
122+ hPutStrLn h $ (show i) ++ " " ++ (show latency)