git.iko.soy/distributed-process
on master help sign in
Start documentation
dd9b0b4Edsko de Vries
benchmarks/Makefile.JustPing+12−5
@@ -3,6 +3,13 @@
3NUMPINGS=100000
3NUMPINGS=100000
4INCLUDES=-i../network-transport/src -i../network-transport-pipes/src
4INCLUDES=-i../network-transport/src -i../network-transport-pipes/src
5
5
6+# Enable for profiling
7+# PROF_GHC=-prof -fprof-auto
8+# PROF_EXE=+RTS -pa -RTS
9+
10+GCC=gcc -std=c99
11+GHC=ghc -rtsopts
12+
6Indirection.ps: JustPingWithHeader.data \
13Indirection.ps: JustPingWithHeader.data \
7 JustPingThroughChan.data \
14 JustPingThroughChan.data \
8 JustPingThroughMVar.data \
15 JustPingThroughMVar.data \
@@ -20,19 +27,19 @@ Headers.ps: JustPingC.data \
20 gnuplot ./Headers.gnuplot
27 gnuplot ./Headers.gnuplot
21
28
22JustPingC.exe: JustPingC.c
29JustPingC.exe: JustPingC.c
23− gcc -o JustPingC.exe -O2 -std=c99 JustPingC.c
30+ $(GCC) -O2 -o JustPingC.exe JustPingC.c
24
31
25JustPingHaskellNT.exe: JustPingHaskell.hs
32JustPingHaskellNT.exe: JustPingHaskell.hs
26− ghc -o JustPingHaskellNT.exe -O2 JustPingHaskell.hs
33+ $(GHC) -O2 $(PROF_GHC) -o JustPingHaskellNT.exe JustPingHaskell.hs
27
34
28JustPingTwoSocketPairsND.data: JustPingTwoSocketPairs.exe
35JustPingTwoSocketPairsND.data: JustPingTwoSocketPairs.exe
29− time ./$< $(NUMPINGS) --NoDelay 2>$@
36+ time ./$< $(NUMPINGS) --NoDelay $(PROF_EXE) 2>$@
30
37
31%.data : %.exe
38%.data : %.exe
32− time ./$< $(NUMPINGS) 2>$@
39+ time ./$< $(NUMPINGS) $(PROF_EXE) 2>$@
33
40
34%.exe :: %.hs
41%.exe :: %.hs
35− ghc -o $@ -O2 -threaded --make -rtsopts $< $(INCLUDES)
42+ $(GHC) -O2 $(PROF_GHC) -threaded -o $@ --make $< $(INCLUDES)
36
43
37.PHONY: clean
44.PHONY: clean
38clean:
45clean:
doc/InternalsTransportTCP.txtnew file+81
@@ -0,0 +1,81 @@
1+The TCP Transport
2+=================
3+
4+Overview
5+--------
6+
7+When a TCP transport is created a new server is started which listens on a
8+given port number on the local host. In order to support multiple connections
9+the transport maintains a set of channels, one per connection, represented as a
10+pair of a `Chan [ByteString]` and a list of pairs `(ThreadId, Socket)` of
11+threads that are listening on this channel. A source end then corresponds to a
12+hostname (the hostname used by clients to identity the local host), port
13+number, and channel ID; a receive end simply corresponds to a channel ID.
14+
15+When `mkTransport` creates a new transport it spawns a thread that listens for
16+incoming connections, running `procConnections` (see below). The set of
17+channels (connections) associated with the transport is initialized to be
18+empty.
19+
20+`newConnectionWith` creates a new channel and add its to the transport channel
21+map (with an empty list of associated threads).
22+
23+To serialize the source end we encode the triple of the local host name, port
24+number, and channel ID, and to deserialize we just decode the same triple
25+(deserialize does not need any other properties of the TCP transport).
26+
27+To connect to the source end we create a new socket, connect to the server at
28+the IP address specified in the TCPConfig, and send the channel number over the
29+connection. Then to `closeSourceEnd` we simply close the socket, and to send a
30+bunch of byte strings we output them on the socket.
31+
32+To receive from the target end we just read from the channel associated with
33+the target end. To `closeTargetEnd` we find kill all threads associated with
34+the channel and close their sockets.
35+
36+When somebody connects to server (running `procConnections`), he first sends a
37+channel ID. `procConnections` then spawns a new thread running
38+`procMessages` which listens for bytestrings on the socket and output them on
39+the specified channel. The ID of this new thread (and the socket it uses) are
40+added to the channel map of the transport.
41+
42+`closeTransport` kills the server thread and all threads that were listening on
43+the channels associated with the transport, and closes all associated sockets.
44+
45+Improving Latency
46+-----------------
47+
48+A series of benchmarks has shown that
49+
50+* The use of `-threaded` triples the latency.
51+
52+* Prepending a header to messages has a negligible effect on latency, even when
53+ sending very small packets. However, the way that we turn the length from an
54+ `Int32` to a `ByteString` _does_ have a significant impact; in particular,
55+ using `Data.Serialize` is very slow (and using Blaze.ByteString not much
56+ better). This is fast:
57+
58+ foreign import ccall unsafe "htonl" htonl :: CInt -> CInt
59+
60+ encodeLength :: Int32 -> IO ByteString
61+ encodeLength i32 =
62+ BSI.create 4 $ \p ->
63+ pokeByteOff p 0 (htonl (fromIntegral i32))
64+
65+* We do not need to use `blaze-builder` or related;
66+ `Network.Socket.Bytestring.sendMany` uses vectored I/O. On the client side
67+ doing a single `recv` to try and read the message header and message, rather
68+ one to read the header and one to read the payload improves latency, but only
69+ by a tiny amount.
70+
71+* Indirection through an `MVar` or a `Chan` does not have an observable effect
72+ on latency.
73+
74+* When two nodes _A_ and _B_ communicate, latency is worse when they
75+ communicate over two pairs of sockets (used unidirectionally) rather than one
76+ pair (used bidirectionally) by about 20%. This is not improved by using
77+ `TCP_NODELAY`, and might be because [acknowledgements cannot piggyback with
78+ payload][1] this way. It might thus be worthwhile to try and reuse TCP
79+ connections (or use UDP).
80+
81+[1]: http://lists.freebsd.org/pipermail/freebsd-hackers/2009-March/028006.html "2 uni-directional TCP connection good?"