git.iko.soy/distributed-process
on master help sign in
Fast (de)serialization of ints

With this new implementation the various implementations are much closer. JustPingOneRecv improves on the basic header implementation by doing a single receive; just is perhaps *slightly* faster, but hardly noticable. JustPingCacheHeader "improves" on that further by faking the header generation (no actual serialization or deserialzation takes place). This provides no further improvement, suggesting that the new implementation of serialization is fast.

commita050b8ad43e78a24704942a566b9884b3480506b authorEdsko de Vries <edsko@well-typed.com> dateFri 13 Apr 2012, 12:17 (15 years ago) parent8049006
6 files changed+81−30
benchmarks/JustPing.gnuplot+2−2 benchmarks/JustPingCacheHeader.hs+24−6 benchmarks/JustPingHaskell.hs−2 benchmarks/JustPingOneRecv.hs+25−8 benchmarks/JustPingNaiveHeader.hs → benchmarks/JustPingWithHeader.hs+24−6 benchmarks/Makefile.JustPing+6−6
benchmarks/JustPing.gnuplot+2−2
@@ -2,7 +2,7 @@ set title "Roundtrip (us)"
22set yrange [0:200]
33plot "JustPingHaskell.data" smooth bezier with lines title "JustPingHaskell (NOT -threaded)", \
44 "JustPingHaskell2.data" smooth bezier with lines title "JustPingHaskell", \
5− "JustPingNaiveHeader.data" smooth bezier with lines title "JustPingNaiveHeader", \
5+ "JustPingWithHeader.data" smooth bezier with lines title "JustPingWithHeader", \
66 "JustPingOneRecv.data" smooth bezier with lines title "JustPingOneRecv", \
77 "JustPingCacheHeader.data" smooth bezier with lines title "JustPingCacheHeader", \
88 "JustPingC.data" smooth bezier with lines title "JustPingC"
@@ -10,7 +10,7 @@ set terminal postscript color
1010set output "JustPing.ps"
1111plot "JustPingHaskell.data" smooth bezier with lines title "JustPingHaskell (NOT -threaded)", \
1212 "JustPingHaskell2.data" smooth bezier with lines title "JustPingHaskell", \
13− "JustPingNaiveHeader.data" smooth bezier with lines title "JustPingNaiveHeader", \
13+ "JustPingWithHeader.data" smooth bezier with lines title "JustPingWithHeader", \
1414 "JustPingOneRecv.data" smooth bezier with lines title "JustPingOneRecv", \
1515 "JustPingCacheHeader.data" smooth bezier with lines title "JustPingCacheHeader", \
1616 "JustPingC.data" smooth bezier with lines title "JustPingC"
benchmarks/JustPingCacheHeader.hs+24−6
@@ -5,8 +5,6 @@ module Main where
55import Control.Monad
66
77import Data.Int
8−import qualified Data.Serialize as Ser
9−import Data.Word (Word8)
108import Network.Socket
119 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
1210 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -25,6 +23,13 @@ import Data.ByteString.Char8 (pack, unpack)
2523import qualified Data.ByteString as BS
2624import qualified Network.Socket.ByteString as NBS
2725import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
26+import Data.ByteString.Internal as BSI
27+import Foreign.Storable (pokeByteOff, peekByteOff)
28+import Foreign.C (CInt(..))
29+import Foreign.ForeignPtr (withForeignPtr)
30+
31+foreign import ccall unsafe "htonl" htonl :: CInt -> CInt
32+foreign import ccall unsafe "ntohl" ntohl :: CInt -> CInt
2833
2934main :: IO ()
3035main = do
@@ -102,15 +107,28 @@ pong sock = do
102107recv :: Socket -> Int -> IO ByteString
103108recv sock i = do
104109 (header, payload) <- BS.splitAt 4 `fmap` NBS.recv sock (4 + i)
105− case Ser.decode header :: Either String Int32 of
106− Left _ -> error "Could not decode header"
107− Right _ -> return payload
110+ -- Ignore header
111+ return payload
108112
109113-- | Cached header
110114header :: ByteString
111−header = Ser.encode (8 :: Int32)
115+header = pack "fake"
112116
113117-- | Wrapper around NBS.send (for profiling)
114118send :: Socket -> ByteString -> IO ()
115119send sock bs = do
116120 NBS.sendMany sock [header, bs]
121+
122+-- | Encode length (manual for now)
123+encodeLength :: Int32 -> IO ByteString
124+encodeLength i32 =
125+ BSI.create 4 $ \p ->
126+ pokeByteOff p 0 (htonl (fromIntegral i32))
127+
128+-- | Decode length (manual for now)
129+decodeLength :: ByteString -> IO Int32
130+decodeLength bs =
131+ let (fp, _, _) = BSI.toForeignPtr bs in
132+ withForeignPtr fp $ \p -> do
133+ w32 <- peekByteOff p 0
134+ return (fromIntegral (ntohl w32))
benchmarks/JustPingHaskell.hs−2
@@ -5,8 +5,6 @@ module Main where
55import Control.Monad
66
77import Data.Int
8−import qualified Data.Serialize as Ser
9−import Data.Word (Word8)
108import Network.Socket
119 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
1210 , SocketType (Stream), SocketOption (ReuseAddr)
benchmarks/JustPingOneRecv.hs+25−8
@@ -5,8 +5,6 @@ module Main where
55import Control.Monad
66
77import Data.Int
8−import qualified Data.Serialize as Ser
9−import Data.Word (Word8)
108import Network.Socket
119 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
1210 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -25,6 +23,13 @@ import Data.ByteString.Char8 (pack, unpack)
2523import qualified Data.ByteString as BS
2624import qualified Network.Socket.ByteString as NBS
2725import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
26+import Data.ByteString.Internal as BSI
27+import Foreign.Storable (pokeByteOff, peekByteOff)
28+import Foreign.C (CInt(..))
29+import Foreign.ForeignPtr (withForeignPtr)
30+
31+foreign import ccall unsafe "htonl" htonl :: CInt -> CInt
32+foreign import ccall unsafe "ntohl" ntohl :: CInt -> CInt
2833
2934main :: IO ()
3035main = do
@@ -102,13 +107,25 @@ pong sock = do
102107recv :: Socket -> Int -> IO ByteString
103108recv sock i = do
104109 (header, payload) <- BS.splitAt 4 `fmap` NBS.recv sock (4 + i)
105− case Ser.decode header :: Either String Int32 of
106− Left _ -> error "Could not decode header"
107− Right _ -> return payload
110+ length <- decodeLength header -- Ignored
111+ return payload
108112
109113-- | Wrapper around NBS.send (for profiling)
110114send :: Socket -> ByteString -> IO ()
111115send sock bs = do
112− let length :: Int32
113− length = fromIntegral $ BS.length bs
114− NBS.sendMany sock [Ser.encode length, bs]
116+ length <- encodeLength (fromIntegral (BS.length bs))
117+ NBS.sendMany sock [length, bs]
118+
119+-- | Encode length (manual for now)
120+encodeLength :: Int32 -> IO ByteString
121+encodeLength i32 =
122+ BSI.create 4 $ \p ->
123+ pokeByteOff p 0 (htonl (fromIntegral i32))
124+
125+-- | Decode length (manual for now)
126+decodeLength :: ByteString -> IO Int32
127+decodeLength bs =
128+ let (fp, _, _) = BSI.toForeignPtr bs in
129+ withForeignPtr fp $ \p -> do
130+ w32 <- peekByteOff p 0
131+ return (fromIntegral (ntohl w32))
benchmarks/JustPingNaiveHeader.hs → benchmarks/JustPingWithHeader.hsrenamed+24−6
@@ -5,8 +5,6 @@ module Main where
55import Control.Monad
66
77import Data.Int
8−import qualified Data.Serialize as Ser
9−import Data.Word (Word8)
108import Network.Socket
119 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
1210 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -25,6 +23,13 @@ import Data.ByteString.Char8 (pack, unpack)
2523import qualified Data.ByteString as BS
2624import qualified Network.Socket.ByteString as NBS
2725import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
26+import Data.ByteString.Internal as BSI
27+import Foreign.Storable (pokeByteOff, peekByteOff)
28+import Foreign.C (CInt(..))
29+import Foreign.ForeignPtr (withForeignPtr)
30+
31+foreign import ccall unsafe "htonl" htonl :: CInt -> CInt
32+foreign import ccall unsafe "ntohl" ntohl :: CInt -> CInt
2833
2934main :: IO ()
3035main = do
@@ -102,12 +107,25 @@ pong sock = do
102107recv :: Socket -> Int -> IO ByteString
103108recv sock _ = do
104109 header <- NBS.recv sock 4
105− let Right length = Ser.decode header
110+ length <- decodeLength header
106111 NBS.recv sock (fromIntegral (length :: Int32))
107112
108113-- | Wrapper around NBS.send (for profiling)
109114send :: Socket -> ByteString -> IO ()
110115send sock bs = do
111− let length :: Int32
112− length = fromIntegral $ BS.length bs
113− NBS.sendMany sock [Ser.encode length, bs]
116+ length <- encodeLength (fromIntegral (BS.length bs))
117+ NBS.sendMany sock [length, bs]
118+
119+-- | Encode length (manual for now)
120+encodeLength :: Int32 -> IO ByteString
121+encodeLength i32 =
122+ BSI.create 4 $ \p ->
123+ pokeByteOff p 0 (htonl (fromIntegral i32))
124+
125+-- | Decode length (manual for now)
126+decodeLength :: ByteString -> IO Int32
127+decodeLength bs =
128+ let (fp, _, _) = BSI.toForeignPtr bs in
129+ withForeignPtr fp $ \p -> do
130+ w32 <- peekByteOff p 0
131+ return (fromIntegral (ntohl w32))
benchmarks/Makefile.JustPing+6−6
@@ -6,7 +6,7 @@ NUMPINGS=100000
66run: JustPingC.data \
77 JustPingHaskell.data \
88 JustPingHaskell2.data \
9− JustPingNaiveHeader.data \
9+ JustPingWithHeader.data \
1010 JustPingOneRecv.data \
1111 JustPingCacheHeader.data
1212 gnuplot ./JustPing.gnuplot
@@ -20,8 +20,8 @@ JustPingHaskell.data: JustPingHaskell
2020JustPingHaskell2.data: JustPingHaskell2
2121 time ./JustPingHaskell2 $(NUMPINGS) 2>JustPingHaskell2.data
2222
23−JustPingNaiveHeader.data: JustPingNaiveHeader
24− time ./JustPingNaiveHeader $(NUMPINGS) 2>JustPingNaiveHeader.data
23+JustPingWithHeader.data: JustPingWithHeader
24+ time ./JustPingWithHeader $(NUMPINGS) 2>JustPingWithHeader.data
2525
2626JustPingOneRecv.data: JustPingOneRecv
2727 time ./JustPingOneRecv $(NUMPINGS) 2>JustPingOneRecv.data
@@ -38,8 +38,8 @@ JustPingHaskell: JustPingHaskell.hs
3838JustPingHaskell2: JustPingHaskell.hs
3939 ghc -o JustPingHaskell2 -O2 -threaded JustPingHaskell.hs
4040
41−JustPingNaiveHeader: JustPingNaiveHeader.hs
42− ghc -o JustPingNaiveHeader -O2 -threaded JustPingNaiveHeader.hs
41+JustPingWithHeader: JustPingWithHeader.hs
42+ ghc -o JustPingWithHeader -O2 -threaded JustPingWithHeader.hs
4343
4444JustPingOneRecv: JustPingOneRecv.hs
4545 ghc -o JustPingOneRecv -O2 -threaded JustPingOneRecv.hs
@@ -49,6 +49,6 @@ JustPingCacheHeader: JustPingCacheHeader.hs
4949
5050.PHONY: clean
5151clean:
52− rm -f *.data *.ps *.pdf JustPingC JustPingHaskell JustPingHaskell2 JustPingNaiveHeader JustPingOneRecv JustPingCacheHeader
52+ rm -f *.data *.ps *.pdf *.o *.hi JustPingC JustPingHaskell JustPingHaskell2 JustPingWithHeader JustPingOneRecv JustPingCacheHeader
5353
5454# vi:set noexpandtab: