Improve SocketReadWrite microbenchmark
Contributed-by: sergey.kuksenko@oracle.com
--- a/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java Wed Feb 13 14:43:25 2019 +0000
+++ b/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java Fri Feb 15 10:39:45 2019 +0000
@@ -22,65 +22,206 @@
*/
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.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import org.openjdk.jmh.annotations.*;
-
/**
* Tests socket read/write.
*/
+
@BenchmarkMode(Mode.Throughput)
-@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class SocketReadWrite {
- private Socket s1, s2;
- private InputStream in;
- private OutputStream out;
+ static final InetAddress address = InetAddress.getLoopbackAddress();
+ public static final int TIMEOUT = 10000;
+
+ static class EchoServer implements Runnable {
+ 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;
- @Setup
- public void beforeRun() throws IOException {
- InetAddress lb = InetAddress.getLoopbackAddress();
- try (ServerSocket ss = new ServerSocket(0)) {
- s1 = new Socket(lb, ss.getLocalPort());
- s2 = ss.accept();
+ 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;
}
- s1.setTcpNoDelay(true);
- s2.setTcpNoDelay(true);
- in = s1.getInputStream();
- out = s2.getOutputStream();
+
+ 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() {
+ if (size == 1) {
+ while (!isDone) {
+ try {
+ int b = this.in.read();
+ out.write(b);
+ } catch (IOException e) {
+ if (!isDone) {
+ e.printStackTrace();
+ }
+ }
+ }
+ } else {
+ 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();
+ }
+
+ }
}
- @TearDown
- public void afterRun() throws IOException {
- s1.close();
- s2.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;
- private final byte[] array = new byte[512*1024];
+ @Param({"false", "true"})
+ public boolean timeout;
+
+ Socket s;
+ InputStream in;
+ OutputStream out;
+ byte[] array;
+
+ @Setup
+ 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);
+ }
+ in = s.getInputStream();
+ out = s.getOutputStream();
+ array = new byte[size];
+ }
+
+ @TearDown
+ public void tearDown() throws IOException {
+ server.close();
+ s.close();
+ }
@Benchmark
- public void test() throws IOException {
+ public void echo() throws IOException {
if (size == 1) {
out.write((byte) 47);
int c = in.read();
} else {
- out.write(array, 0, size);
- int nread = 0;
- while (nread < size) {
- int n = in.read(array, 0, size);
- if (n < 0) throw new RuntimeException();
- nread += n;
- }
+ out.write(array);
+ readN(array, size, in);
}
+
}
}