git.iko.soy/distributed-process
on master help sign in
Barebones ping test. Haskell approximates C.
4e2c09fEdsko de Vries
benchmarks/JustPing.gnuplotnew file+4
@@ -0,0 +1,4 @@
1+set title "Roundtrip (us)"
2+set yrange [0:300]
3+plot "JustPingHaskell.data" every 10::5 title "Haskell (TCP)", \
4+ "JustPingC.data" every 10::0 title "C"
benchmarks/JustPingC.cnew file+150
@@ -0,0 +1,150 @@
1+#include <stdio.h>
2+#include <stdlib.h>
3+#include <sys/types.h>
4+#include <sys/socket.h>
5+#include <netdb.h>
6+#include <string.h>
7+#include <unistd.h>
8+#include <signal.h>
9+#include <errno.h>
10+#include <time.h>
11+#include <sys/time.h>
12+#include <math.h>
13+
14+// Note: this is not consistent across CPUs (and hence across threads on multicore machines)
15+double timestamp() {
16+ struct timeval tp;
17+ gettimeofday(&tp, NULL);
18+ return ((double) tp.tv_sec) * 1e6 + (double) tp.tv_usec;
19+}
20+
21+int server() {
22+ printf("starting server\n");
23+
24+ struct addrinfo hints, *res;
25+ int error, server_socket;
26+
27+ memset(&hints, 0, sizeof(hints));
28+ hints.ai_family = PF_INET;
29+ hints.ai_socktype = SOCK_STREAM;
30+ hints.ai_flags = AI_PASSIVE;
31+
32+ error = getaddrinfo(NULL, "8080", &hints, &res);
33+ if(error) {
34+ printf("server error: %s\n", gai_strerror(error));
35+ return -1;
36+ }
37+
38+ server_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
39+ if(server_socket < 0) {
40+ printf("server error: could not create socket\n");
41+ return -1;
42+ }
43+
44+ int yes = 1;
45+ if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
46+ printf("server error: could not set socket options\n");
47+ return -1;
48+ }
49+
50+ if(bind(server_socket, res->ai_addr, res->ai_addrlen) < 0) {
51+ printf("server error: could not bind to socket\n");
52+ return -1;
53+ }
54+
55+ listen(server_socket, 5);
56+
57+ int client_socket;
58+ struct sockaddr_storage client_addr;
59+ socklen_t addr_size;
60+ client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &addr_size);
61+
62+ for(;;) {
63+ char* buf = malloc(8);
64+ ssize_t read = recv(client_socket, buf, 8, 0);
65+ if(read == 0) {
66+ free(buf);
67+ break;
68+ }
69+ // printf("server received '%s'\n", buf);
70+ send(client_socket, buf, 8, 0);
71+ free(buf);
72+ }
73+
74+ freeaddrinfo(res);
75+ return 0;
76+}
77+
78+int client(int pings) {
79+ printf("starting client\n");
80+
81+ struct addrinfo hints, *res;
82+ int error, client_socket;
83+
84+ memset(&hints, 0, sizeof(hints));
85+ hints.ai_family = PF_INET;
86+ hints.ai_socktype = SOCK_STREAM;
87+
88+ error = getaddrinfo("127.0.0.1", "8080", &hints, &res);
89+ if(error) {
90+ printf("client error: %s\n", gai_strerror(error));
91+ return -1;
92+ }
93+
94+ client_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
95+ if(client_socket < 0) {
96+ printf("client error: could not create socket\n");
97+ return -1;
98+ }
99+
100+ if(connect(client_socket, res->ai_addr, res->ai_addrlen) < 0) {
101+ printf("client error: could not connect: %s\n", strerror(errno));
102+ return -1;
103+ }
104+
105+ for(int i = 0; i < pings; i++) {
106+ double timestamp_before = timestamp();
107+
108+ send(client_socket, "ping123", 8, 0);
109+
110+ char *buf = malloc(8);
111+ ssize_t read = recv(client_socket, buf, 8, 0);
112+
113+ if(read == 0) {
114+ printf("server exited prematurely\n");
115+ free(buf);
116+ break;
117+ }
118+
119+ // printf("client received '%s'\n", buf);
120+ free(buf);
121+
122+ double timestamp_after = timestamp();
123+ fprintf(stderr, "%i %lf\n", i, timestamp_after - timestamp_before);
124+ }
125+
126+ printf("client did %d pings\n", pings);
127+
128+ freeaddrinfo(res);
129+ return 0;
130+}
131+
132+int usage(int argc, char** argv) {
133+ printf("usage: %s <number of pings>\n", argv[0]);
134+ return -1;
135+}
136+
137+int main(int argc, char** argv) {
138+ if(argc != 2) {
139+ return usage(argc, argv);
140+ }
141+
142+ if(fork() == 0) {
143+ // TODO: we should wait until we know the server is ready
144+ int pings = 0;
145+ sscanf(argv[1], "%d", &pings);
146+ return client(pings);
147+ } else {
148+ return server();
149+ }
150+}
benchmarks/JustPingHaskell.hsnew file+101
@@ -0,0 +1,101 @@
1+{-# LANGUAGE CPP, BangPatterns #-}
2+
3+module Main where
4+
5+import Control.Monad
6+
7+import Data.Int
8+import qualified Data.Serialize as Ser
9+import Data.Word (Word8)
10+import Network.Socket
11+ ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
12+ , SocketType (Stream), SocketOption (ReuseAddr)
13+ , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
14+ , defaultHints
15+ , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
16+import System.Environment (getArgs, withArgs)
17+import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
18+import System.IO (withFile, IOMode(..), hPutStrLn, Handle, stderr)
19+import Control.Concurrent (forkIO)
20+import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
21+import qualified Network.Socket as N
22+import Debug.Trace
23+import Data.ByteString (ByteString)
24+import Data.ByteString.Char8 (pack, unpack)
25+import qualified Data.ByteString as BS
26+import qualified Network.Socket.ByteString as NBS
27+import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
28+
29+main :: IO ()
30+main = do
31+ [pingsStr] <- getArgs
32+ serverReady <- newEmptyMVar
33+
34+ -- Start the server
35+ forkIO $ do
36+ putStrLn "server: creating TCP connection"
37+ serverAddrs <- getAddrInfo
38+ (Just (defaultHints { addrFlags = [AI_PASSIVE] } ))
39+ Nothing
40+ (Just "8080")
41+ let serverAddr = head serverAddrs
42+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
43+ setSocketOption sock ReuseAddr 1
44+ bindSocket sock (addrAddress serverAddr)
45+
46+ putStrLn "server: awaiting client connection"
47+ putMVar serverReady ()
48+ listen sock 1
49+ (clientSock, clientAddr) <- accept sock
50+
51+ putStrLn "server: listening for pings"
52+ pong clientSock
53+
54+ -- Client
55+ takeMVar serverReady
56+ let pings = read pingsStr
57+ serverAddrs <- getAddrInfo
58+ Nothing
59+ (Just "127.0.0.1")
60+ (Just "8080")
61+ let serverAddr = head serverAddrs
62+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
63+
64+ N.connect sock (addrAddress serverAddr)
65+
66+ ping sock pings
67+
68+pingMessage :: ByteString
69+pingMessage = pack "ping123"
70+
71+ping :: Socket -> Int -> IO ()
72+ping sock pings = go pings
73+ where
74+ go :: Int -> IO ()
75+ go 0 = do
76+ putStrLn $ "client did " ++ show pings ++ " pings"
77+ go !i = do
78+ before <- getCurrentTime
79+ send sock pingMessage
80+ bs <- recv sock 8
81+ after <- getCurrentTime
82+ -- putStrLn $ "client received " ++ unpack bs
83+ let latency = (1e6 :: Double) * realToFrac (diffUTCTime after before)
84+ hPutStrLn stderr $ show i ++ " " ++ show latency
85+ go (i - 1)
86+
87+pong :: Socket -> IO ()
88+pong sock = do
89+ bs <- recv sock 8
90+ -- putStrLn $ "server received " ++ unpack bs
91+ when (BS.length bs > 0) $ do
92+ send sock bs
93+ pong sock
94+
95+-- | Wrapper around NBS.recv (for profiling)
96+recv :: Socket -> Int -> IO ByteString
97+recv = NBS.recv
98+
99+-- | Wrapper around NBS.send (for profiling)
100+send :: Socket -> ByteString -> IO Int
101+send = NBS.send
benchmarks/Makefile+1−1
@@ -9,7 +9,7 @@ trials = 1
9INCLUDES= -i../network-transport/src -i../network-transport-pipes/src
9INCLUDES= -i../network-transport/src -i../network-transport-pipes/src
10
10
11# Enable for profiling
11# Enable for profiling
12−# PROFILING=-prof -fprof-auto
12+PROFILING=-prof -fprof-auto
13
13
14ifeq ($(GHC),)
14ifeq ($(GHC),)
15 GHC=ghc
15 GHC=ghc
benchmarks/Makefile.JustPingnew file+21
@@ -0,0 +1,21 @@
1+# The "Just Ping" tests measure ping latency with as little overhead as possible
2+
3+NUMPINGS=10000
4+
5+.PHONY: run
6+run: JustPingC JustPingHaskell
7+ time ./JustPingC $(NUMPINGS) 2>JustPingC.data
8+ time ./JustPingHaskell $(NUMPINGS) 2>JustPingHaskell.data
9+ gnuplot ./JustPing.gnuplot
10+
11+JustPingC: JustPingC.c
12+ gcc -o JustPingC -O2 -std=c99 JustPingC.c
13+
14+JustPingHaskell: JustPingHaskell.hs
15+ ghc -o JustPingHaskell -O2 JustPingHaskell.hs
16+
17+.PHONY: clean
18+clean:
19+ rm -f JustPingC JustPingHaskell
20+
21+# vi:set noexpandtab:
benchmarks/PingTCP.hs+6−4
@@ -15,7 +15,7 @@ import Network.Socket
15 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
15 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
16import System.Environment (getArgs, withArgs)
16import System.Environment (getArgs, withArgs)
17import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
17import Data.Time (getCurrentTime, diffUTCTime, NominalDiffTime)
18−import System.IO (withFile, IOMode(..), hPutStrLn)
18+import System.IO (withFile, IOMode(..), hPutStrLn, Handle)
19import Control.Concurrent (forkIO)
19import Control.Concurrent (forkIO)
20import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
20import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
21
21
@@ -103,9 +103,7 @@ ping sock pings = go [] pings
103 where
103 where
104 go :: [Double] -> Int -> IO ()
104 go :: [Double] -> Int -> IO ()
105 go rtl 0 = do
105 go rtl 0 = do
106− withFile "round-trip-latency-tcp.data" WriteMode $ \h ->
106+ withFile "round-trip-latency-tcp.data" WriteMode (writeData rtl)
107− forM_ (zip [0..] rtl) $ \(i, latency) ->
108− hPutStrLn h $ (show i) ++ " " ++ (show latency)
109 putStrLn $ "client did " ++ show pings ++ " pings"
107 putStrLn $ "client did " ++ show pings ++ " pings"
110 go rtl !i = do
108 go rtl !i = do
111 before <- getCurrentTime
109 before <- getCurrentTime
@@ -114,6 +112,10 @@ ping sock pings = go [] pings
114 after <- getCurrentTime
112 after <- getCurrentTime
115 let latency = (1e6 :: Double) * realToFrac (diffUTCTime after before)
113 let latency = (1e6 :: Double) * realToFrac (diffUTCTime after before)
116 latency `seq` go (latency : rtl) (i - 1)
114 latency `seq` go (latency : rtl) (i - 1)
115+ writeData :: [Double] -> Handle -> IO ()
116+ writeData rtl h = forM_ (zip [0..] rtl) (writeDataLine h)
117+ writeDataLine :: Handle -> (Int, Double) -> IO ()
118+ writeDataLine h (i, latency) = hPutStrLn h $ (show i) ++ " " ++ (show latency)
117
119
118pong :: Socket -> IO ()
120pong :: Socket -> IO ()
119pong sock = do
121pong sock = do