git.iko.soy/distributed-process
on master help sign in
added switch between lazy and strict
commitbb31a08fe82d2c5ffe881fc193c00b72138461a9 authorAdam Foltzer <acfoltzer@gmail.com> dateThu 23 Feb 2012, 14:51 (15 years ago) parent8bdb68c
8 files changed+165−47
benchmarks/PingTCP.hs+26−7 benchmarks/PingTransport.hs+19−1 benchmarks/SendTCP.hs+23−5 benchmarks/SendTransport.hs+22−6 network-transport/network-transport.cabal−2 network-transport/src/Network/Transport.hs+6 network-transport/src/Network/Transport/MVar.hs+18−3 network-transport/src/Network/Transport/TCP.hs+51−23
benchmarks/PingTCP.hs+26−7
@@ -1,9 +1,13 @@
1+{-# LANGUAGE CPP #-}
2+
13module Main where
24
35import Control.Monad
46import Criterion.Main (Benchmark, bench, defaultMain, nfIO, bgroup)
5−import Data.Binary
7+
68import Data.Int
9+import qualified Data.Serialize as Ser
10+import Data.Word (Word8)
711import Network.Socket
812 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
913 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -13,10 +17,25 @@ import Network.Socket
1317import System.Environment (getArgs, withArgs)
1418
1519import qualified Network.Socket as N
16−import qualified Network.Socket.ByteString.Lazy as NBS
1720
1821import Debug.Trace
1922
23+#ifndef LAZY
24+import Data.ByteString (ByteString)
25+import qualified Network.Socket.ByteString as NBS
26+encode = Ser.encode
27+decode = Ser.decode
28+#else
29+import Data.ByteString.Lazy (ByteString)
30+import qualified Network.Socket.ByteString.Lazy as NBS
31+encode = Ser.encodeLazy
32+decode = Ser.decodeLazy
33+#endif
34+{-# INLINE encode #-}
35+{-# INLINE decode #-}
36+encode :: Ser.Serialize a => a -> ByteString
37+decode :: Ser.Serialize a => ByteString -> Either String a
38+
2039-- | This performs a ping benchmark on a TCP connection created by
2140-- Network.Socket. To compile this file, you might use:
2241--
@@ -70,15 +89,15 @@ main = do
7089
7190-- | Each `ping` sends a single byte, and expects to receive one
7291-- back in return.
73−ping :: Socket -> IO Word8
92+ping :: Socket -> IO Int64
7493ping sock = do
75− NBS.send sock $ encode (0 :: Word8)
76− bs <- NBS.recv sock 1
77− return $ decode bs
94+ NBS.send sock $ encode (0 :: Int64)
95+ bs <- NBS.recv sock 8
96+ either error return $ decode bs
7897
7998pong :: Socket -> IO ()
8099pong sock = do
81− bs <- NBS.recv sock 1
100+ bs <- NBS.recv sock 8
82101 NBS.sendAll sock bs
83102 return ()
84103
benchmarks/PingTransport.hs+19−1
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13module Main where
24
35import Network.Transport
@@ -5,12 +7,28 @@ import Network.Transport.TCP (mkTransport, TCPConfig (..))
57
68import Control.Monad (forever, replicateM_)
79import Criterion.Main (Benchmark, bench, defaultMain, nfIO)
8−import Data.Serialize
10+import qualified Data.Serialize as Ser
911import Data.Maybe (fromJust)
1012import Data.Int
1113import System.Environment (getArgs, withArgs)
1214
15+#ifndef LAZY
1316import qualified Data.ByteString.Char8 as BS
17+import qualified Network.Socket.ByteString as NBS
18+encode = Ser.encode
19+decode = Ser.decode
20+#else
21+import qualified Data.ByteString.Lazy.Char8 as BS
22+import qualified Network.Socket.ByteString.Lazy as NBS
23+encode = Ser.encodeLazy
24+decode = Ser.decodeLazy
25+#endif
26+{-# INLINE encode #-}
27+{-# INLINE decode #-}
28+encode :: Ser.Serialize a => a -> BS.ByteString
29+decode :: Ser.Serialize a => BS.ByteString -> Either String a
30+
31+
1432
1533-- | This performs a ping benchmark on the TCP transport. This can be
1634-- compiled using:
benchmarks/SendTCP.hs+23−5
@@ -1,10 +1,12 @@
1+{-# LANGUAGE CPP #-}
2+
13module Main where
24
35import Control.Monad
46import Criterion.Main (Benchmark, bench, defaultMain, nfIO, bgroup)
5−import Data.Binary
6−import Data.ByteString.Lazy (ByteString)
77import Data.Int
8+import qualified Data.Serialize as Ser
9+import Data.Word (Word8)
810import Network.Socket
911 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
1012 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -13,9 +15,25 @@ import Network.Socket
1315 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
1416import System.Environment (getArgs, withArgs)
1517
16−import qualified Data.ByteString.Lazy as BS
1718import qualified Network.Socket as N
19+
20+#ifdef LAZY
21+import Data.ByteString (ByteString)
22+import qualified Data.ByteString as BS
23+import qualified Network.Socket.ByteString as NBS
24+encode = Ser.encode
25+decode = Ser.decode
26+#else
27+import Data.ByteString.Lazy (ByteString)
28+import qualified Data.ByteString.Lazy as BS
1829import qualified Network.Socket.ByteString.Lazy as NBS
30+encode = Ser.encodeLazy
31+decode = Ser.decodeLazy
32+#endif
33+{-# INLINE encode #-}
34+{-# INLINE decode #-}
35+encode :: Ser.Serialize a => a -> ByteString
36+decode :: Ser.Serialize a => ByteString -> Either String a
1937
2038-- | This performs a benchmark on a TCP connection to measure how long it takes
2139-- to transfer a number of bytes.
@@ -78,9 +96,9 @@ ping :: Socket -> ByteString -> IO Word8
7896ping sock bs = do
7997 NBS.send sock bs
8098 bs' <- NBS.recv sock 1
81− return $ decode bs
99+ either error return $ decode bs
82100
83−pong :: Socket -> Int64 -> IO ()
101+-- pong :: Socket -> Int64 -> IO ()
84102pong sock size = do
85103 bs <- NBS.recv sock size
86104 NBS.sendAll sock (encode (0 :: Word8))
benchmarks/SendTransport.hs+22−6
@@ -1,19 +1,37 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoMonomorphismRestriction #-}
3+
14module Main where
25
36import Network.Transport
47import Network.Transport.TCP (mkTransport, TCPConfig (..))
58
6−import Control.DeepSeq (deepseq)
9+import Control.Applicative
710import Control.Monad (forever, replicateM, replicateM_)
811import Criterion.Main (Benchmark, bench, defaultMain, nfIO)
9−import Data.Serialize
10−import Data.ByteString (ByteString)
12+import qualified Data.Serialize as Ser
1113import Data.Maybe (fromJust)
1214import Data.Int
1315import System.Environment (getArgs, withArgs)
1416import System.Random
1517
18+#ifndef LAZY
19+import Data.ByteString (ByteString)
1620import qualified Data.ByteString.Char8 as BS
21+import qualified Network.Socket.ByteString as NBS
22+encode = Ser.encode
23+decode = Ser.decode
24+#else
25+import Data.ByteString.Lazy (ByteString)
26+import qualified Data.ByteString.Lazy.Char8 as BS
27+import qualified Network.Socket.ByteString.Lazy as NBS
28+encode = Ser.encodeLazy
29+decode = Ser.decodeLazy
30+#endif
31+{-# INLINE encode #-}
32+{-# INLINE decode #-}
33+encode :: Ser.Serialize a => a -> ByteString
34+decode :: Ser.Serialize a => ByteString -> Either String a
1735
1836-- | This performs a benchmark on the TCP transport to measure how long
1937-- it takes to transfer a number of bytes. This can be compiled using:
@@ -69,9 +87,7 @@ main = do
6987 send sourceEndPing [serialize sourceAddrPong]
7088
7189 -- benchmark the data
72− rands <- replicateM size randomIO
73− let bs' = BS.pack (rands `deepseq` (rands :: [Char]))
74− bs = BS.unpack bs' `deepseq` bs'
90+ bs <- BS.pack <$> replicateM size randomIO
7591 withArgs args' $ defaultMain [ benchSend sourceEndPing targetEndPong bs ]
7692
7793-- | The effect of `ping sourceEndPing targetEndPong bs` is to send the
network-transport/network-transport.cabal−2
@@ -14,10 +14,8 @@ Library
1414 binary >= 0.5,
1515 bytestring >= 0.9,
1616 cereal >= 0.3,
17− conduit >= 0.2,
1817 containers >= 0.4,
1918 network >= 2.3,
20− network-conduit >= 0.2,
2119 safe >= 0.3
2220 Exposed-modules: Network.Transport,
2321 Network.Transport.MVar,
network-transport/src/Network/Transport.hs+6
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13module Network.Transport
24 ( Hints (..)
35 , TargetEnd (..)
@@ -12,7 +14,11 @@ module Network.Transport
1214 , newMulticast
1315 ) where
1416
17+#ifndef LAZY
1518import Data.ByteString.Char8 (ByteString)
19+#else
20+import Data.ByteString.Lazy.Char8 (ByteString)
21+#endif
1622
1723-- | The `Hints` and `SourceHints` provide hints to the underlying transport
1824-- about the kind of connection that is required. This might include details
network-transport/src/Network/Transport/MVar.hs+18−3
@@ -1,4 +1,5 @@
11{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
23
34module Network.Transport.MVar
45 ( mkTransport
@@ -6,14 +7,28 @@ module Network.Transport.MVar
67
78import Control.Concurrent.MVar
89import Data.IntMap (IntMap)
9−import Data.ByteString (ByteString)
10−import Data.Serialize
10+import qualified Data.Serialize as Ser
1111
1212import qualified Data.IntMap as IntMap
13−import qualified Data.ByteString as BS
1413
1514import Network.Transport
1615
16+#ifndef LAZY
17+import Data.ByteString.Char8 (ByteString)
18+import qualified Data.ByteString.Char8 as BS
19+encode = Ser.encode
20+decode = Ser.decode
21+#else
22+import Data.ByteString.Lazy.Char8 (ByteString)
23+import qualified Data.ByteString.Lazy.Char8 as BS
24+encode = Ser.encodeLazy
25+decode = Ser.decodeLazy
26+#endif
27+{-# INLINE encode #-}
28+{-# INLINE decode #-}
29+encode :: Ser.Serialize a => a -> ByteString
30+decode :: Ser.Serialize a => ByteString -> Either String a
31+
1732type Chans = MVar (Int, IntMap (MVar [ByteString]))
1833
1934mkTransport :: IO Transport
network-transport/src/Network/Transport/TCP.hs+51−23
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13module Network.Transport.TCP
24 ( mkTransport
35 , TCPConfig (..)
@@ -12,19 +14,18 @@ import Control.Concurrent.Chan
1214import Control.Concurrent.MVar
1315import Control.Monad (forever, forM_)
1416
15−import Data.ByteString.Char8 (ByteString)
16−import qualified Data.ByteString.Char8 as BS
17−import Data.ByteString.Internal
17+import qualified Data.ByteString.Internal as BSI
1818
1919import Data.IntMap (IntMap)
2020import qualified Data.IntMap as IntMap
2121
22−import Data.Int
23−import Data.Serialize
22+import Data.Int (Int32)
23+import qualified Data.Serialize as Ser
2424import Data.Word
2525
2626import Foreign.ForeignPtr (withForeignPtr)
2727import Foreign.Ptr (Ptr, plusPtr)
28+import Foreign.Storable (sizeOf)
2829
2930import Network.Socket
3031 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
@@ -32,14 +33,30 @@ import Network.Socket
3233 , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
3334 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
3435import qualified Network.Socket as N
35−import qualified Network.Socket.ByteString as NBS
3636
3737import Text.Printf
3838
3939import Safe
4040
41−import qualified Data.Binary as B
42−
41+#ifndef LAZY
42+import Data.ByteString.Char8 (ByteString)
43+import qualified Data.ByteString.Char8 as BS
44+import qualified Network.Socket.ByteString as NBS
45+sendNBS = NBS.sendMany
46+encode = Ser.encode
47+decode = Ser.decode
48+#else
49+import Data.ByteString.Lazy.Char8 (ByteString)
50+import qualified Data.ByteString.Lazy.Char8 as BS
51+import qualified Network.Socket.ByteString.Lazy as NBS
52+sendNBS sock bs = NBS.sendAll sock $! head bs
53+encode = Ser.encodeLazy
54+decode = Ser.decodeLazy
55+#endif
56+{-# INLINE encode #-}
57+{-# INLINE decode #-}
58+encode :: Ser.Serialize a => a -> ByteString
59+decode :: Ser.Serialize a => ByteString -> Either String a
4360
4461type ChanId = Int
4562type Chans = MVar (ChanId, IntMap (Chan [ByteString], [(ThreadId, Socket)]))
@@ -112,13 +129,13 @@ mkSourceEnd host service chanId = withSocketsDo $ do
112129 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
113130 setSocketOption sock ReuseAddr 1
114131 N.connect sock (addrAddress serverAddr)
115− NBS.sendAll sock $ encode (fromIntegral chanId :: Int64)
132+ NBS.sendAll sock $ encode (fromIntegral chanId :: Int32)
116133 return SourceEnd
117134 { send = {-# SCC "send" #-} \bss -> do
118− let size = fromIntegral (sum . map BS.length $ bss) :: Int64
135+ let size = fromIntegral (sum . map BS.length $ bss) :: Int32
119136 sizeStr | size < 255 = encode (fromIntegral size :: Word8)
120137 | otherwise = BS.cons (toEnum 255) (encode size)
121− NBS.sendMany sock (sizeStr:bss)
138+ sendNBS sock (sizeStr:bss)
122139 , closeSourceEnd = {-# SCC "closeSourceEnd" #-} sClose sock
123140 }
124141
@@ -145,11 +162,11 @@ procConnections chans sock = forever $ do
145162 let err m = error $ printf "procConnections: %s" m
146163 (clientSock, _clientAddr) <- accept sock
147164 -- decode the first message to find the correct chanId
148− bss <- recvExact clientSock 8
165+ bss <- recvExact clientSock (fromIntegral $ sizeOf (0 :: Int32))
149166 case bss of
150167 Left _ -> err "inbound chanId aborted"
151168 Right bs -> do
152− let chanId = either err fromIntegral (decode bs :: Either String Int64)
169+ let chanId = either err fromIntegral (decode bs :: Either String Int32)
153170 (chanId', chanMap) <- takeMVar chans
154171 case IntMap.lookup chanId chanMap of
155172 Nothing -> do
@@ -175,7 +192,7 @@ procMessages chans chanId chan sock = do
175192 Right sizeBS -> do
176193 case decode sizeBS :: Either String Word8 of
177194 Right 255 -> do
178− esizeBS' <- recvExact sock 8
195+ esizeBS' <- recvExact sock (fromIntegral $ sizeOf (0 :: Int32))
179196 case esizeBS' of
180197 Left _ -> closeSocket
181198 Right sizeBS' -> procMessage $ decode sizeBS'
@@ -194,7 +211,7 @@ procMessages chans chanId chan sock = do
194211 let chanMap' = IntMap.insert chanId (chan, socks') chanMap
195212 putMVar chans (chanId', chanMap')
196213 sClose sock
197− procMessage :: Either String Int64 -> IO ()
214+ procMessage :: Either String Int32 -> IO ()
198215 procMessage (Left msg) = err msg
199216 procMessage (Right size) = do
200217 ebs <- recvExact sock size
@@ -216,22 +233,33 @@ procMessages chans chanId chan sock = do
216233-- a partial retrieval since the socket was closed, and `return Right bs`
217234-- indicates success. This hasn't been implemented, since a Transport `receive`
218235-- represents an atomic receipt of a message.
219−recvExact :: Socket -> Int64 -> IO (Either ByteString ByteString)
236+#ifndef LAZY
237+recvExact :: Socket -> Int32 -> IO (Either ByteString ByteString)
220238recvExact sock l = do
221− res <- createAndTrim (fromIntegral l) (go 0)
239+ res <- BSI.createAndTrim (fromIntegral l) (go 0)
222240 case BS.length res of
223241 n | n == (fromIntegral l) -> return $ Right res
224− n -> do
225− printf "recvExact: expected %dB, got %dB\n" l n
226− return $ Left res
242+ n -> return $ Left res
227243 where
228244 go :: Int -> Ptr Word8 -> IO Int
229245 go n ptr | n == (fromIntegral l) = return n
230246 go n ptr = do
231− (p, off, len) <- toForeignPtr <$> NBS.recv sock (fromIntegral l)
247+ (p, off, len) <- BSI.toForeignPtr <$> NBS.recv sock (min (fromIntegral l-n) 4096)
232248 if len == 0
233249 then return n
234250 else withForeignPtr p $ \p -> do
235− memcpy (ptr `plusPtr` n) (p `plusPtr` off) (fromIntegral len)
251+ BSI.memcpy (ptr `plusPtr` n) (p `plusPtr` off) (fromIntegral len)
236252 go (n+len) ptr
237−
253+#else
254+recvExact :: Socket -> Int32 -> IO (Either ByteString ByteString)
255+recvExact sock n =
256+ go [] sock n
257+ where
258+ go :: [ByteString] -> Socket -> Int32 -> IO (Either ByteString ByteString)
259+ go bss _ 0 = return (Right $ BS.concat (reverse bss))
260+ go bss sock n = do
261+ bs <- NBS.recv sock (min (fromIntegral n) 4096)
262+ if BS.null bs
263+ then return (Left $ BS.concat (reverse bss))
264+ else go (bs:bss) sock (n - (fromIntegral $ BS.length bs))
265+#endif