8223921: Update SocketReadWrite benchmark
authorskuksenko
Tue, 14 May 2019 15:09:31 -0700
changeset 54850 f8d45530b75e
parent 54849 1e4ab8f5bc77
child 54851 f67269c129f9
8223921: Update SocketReadWrite benchmark Reviewed-by: redestad
test/micro/org/openjdk/bench/java/net/SocketReadWrite.java
--- a/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java	Tue May 14 22:31:52 2019 +0200
+++ b/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java	Tue May 14 15:09:31 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019 Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,86 +22,194 @@
  */
 package org.openjdk.bench.java.net;
 
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 
-import org.openjdk.jmh.annotations.BenchmarkMode;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OutputTimeUnit;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.TearDown;
-
 /**
- * Tests the overheads of I/O API.
- * This test is known to depend heavily on network conditions and paltform.
+ * Benchmark socket read/write.
+ *
  */
 @BenchmarkMode(Mode.Throughput)
-@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@OutputTimeUnit(TimeUnit.SECONDS)
 @State(Scope.Thread)
 public class SocketReadWrite {
 
-    private OutputStream os;
-    private InputStream is;
-    private ServerSocket ss;
-    private Socket s1, s2;
-    private ReadThread rt;
+    static final InetAddress address = InetAddress.getLoopbackAddress();
+    public static final int TIMEOUT = 10000;
+
+    static class EchoServer implements Runnable {
+        // EchoServer is implemented to execute the same amount echo threads as benchmarking threads are running
+
+        final ServerSocket ss;
+        final int port;
+        final CountDownLatch startedLatch;
+        final int size;
+        final boolean timeout;
+        List<ServerThread> threads = new ArrayList<>();
+        volatile boolean isDone = false;
+
+        public EchoServer(CountDownLatch await, int size, boolean timeout) throws IOException {
+            this.size = size;
+            this.timeout = timeout;
+            ss = new ServerSocket(0);
+            port = ss.getLocalPort();
+            this.startedLatch = await;
+        }
+
+        @Override
+        public void run() {
+            startedLatch.countDown();
+            while (!isDone) {
+                try {
+                    Socket s = ss.accept();
+                    s.setTcpNoDelay(true);
+                    if (timeout) {
+                        s.setSoTimeout(TIMEOUT);
+                    }
+                    ServerThread st = new ServerThread(s, size);
+                    threads.add(st);
+                    new Thread(st).start();
+                } catch (IOException e) {
+                    if (!isDone) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        synchronized void close() throws IOException {
+            if (!isDone) {
+                isDone = true;
+                ss.close();
+                for (ServerThread st : threads) {
+                    st.close();
+                }
+            }
+        }
+
+        static EchoServer instance = null;
+
+        static synchronized EchoServer startServer(int size, boolean timeout) throws IOException {
+            if (instance == null) {
+                CountDownLatch started = new CountDownLatch(1);
+                EchoServer s = new EchoServer(started, size, timeout);
+                new Thread(s).start();
+                try {
+                    started.await(); // wait until server thread started
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                instance = s;
+            }
+            return instance;
+        }
+
+        static class ServerThread implements Runnable {
+
+            final Socket s;
+            final InputStream in;
+            final OutputStream out;
+            final int size;
+            volatile boolean isDone = false;
+
+            ServerThread(Socket s, int size) throws IOException {
+                this.s = s;
+                this.size = size;
+                in = s.getInputStream();
+                out = s.getOutputStream();
+            }
+
+            @Override
+            public void run() {
+                byte[] a = new byte[size];
+                while (!isDone) {
+                    try {
+                        readN(a, size, this.in);
+                        out.write(a);
+                    } catch (IOException e) {
+                        if (!isDone) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            }
+
+            public void close() throws IOException {
+                isDone = true;
+                s.close();
+            }
+
+        }
+    }
+
+    static void readN(byte[] array, int size, InputStream in) throws IOException {
+        int nread = 0;
+        while (size > 0) {
+            int n = in.read(array, nread, size);
+            if (n < 0) throw new RuntimeException();
+            nread += n;
+            size -= n;
+        }
+    }
+
+    EchoServer server;
+
+    @Param({"1", "1024", "8192", "64000", "128000"})
+    public int size;
+
+    @Param({"false", "true"})
+    public boolean timeout;
+
+    Socket s;
+    InputStream in;
+    OutputStream out;
+    byte[] array;
 
     @Setup
-    public void beforeRun() throws IOException {
-        InetAddress iaddr = InetAddress.getLocalHost();
-
-        ss = new ServerSocket(0);
-        s1 = new Socket(iaddr, ss.getLocalPort());
-        s2 = ss.accept();
-
-        os = s1.getOutputStream();
-        is = s2.getInputStream();
-
-        rt = new ReadThread(is);
-        rt.start();
+    public void setup() throws IOException {
+        server = EchoServer.startServer(size, timeout);
+        int port = server.port;
+        s = new Socket(address, port);
+        s.setTcpNoDelay(true);
+        if (timeout) {
+            s.setSoTimeout(TIMEOUT);
+            // 10 seconds times is quite large and never will happen (for microbenchmarking),
+            // but it's required since other paths inside SocketImpl are involved
+        }
+        in = s.getInputStream();
+        out = s.getOutputStream();
+        array = new byte[size];
+        ThreadLocalRandom.current().nextBytes(array);
     }
 
     @TearDown
-    public void afterRun() throws IOException, InterruptedException {
-        os.write(0);
-        os.close();
-        is.close();
-        s1.close();
-        s2.close();
-        ss.close();
-        rt.join();
+    public void tearDown() throws IOException {
+        server.close();
+        s.close();
     }
 
     @Benchmark
-    public void test() throws IOException {
-        os.write((byte) 4711);
+    public void echo() throws IOException {
+        out.write(array);
+        readN(array, size, in);
     }
-
-    static class ReadThread extends Thread {
-        private InputStream is;
-
-        public ReadThread(InputStream is) {
-            this.is = is;
-        }
-
-        public void run() {
-            try {
-                while (is.read() > 0);
-            } catch (SocketException ex) {
-                // ignore - most likely "socket closed", which means shutdown
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
 }