1 /* 2 * Copyright (c) 2014, 2019 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.bench.java.net; 24 25 import org.openjdk.jmh.annotations.Benchmark; 26 import org.openjdk.jmh.annotations.BenchmarkMode; 27 import org.openjdk.jmh.annotations.Mode; 28 import org.openjdk.jmh.annotations.OutputTimeUnit; 29 import org.openjdk.jmh.annotations.Param; 30 import org.openjdk.jmh.annotations.Scope; 31 import org.openjdk.jmh.annotations.Setup; 32 import org.openjdk.jmh.annotations.State; 33 import org.openjdk.jmh.annotations.TearDown; 34 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.OutputStream; 38 import java.net.InetAddress; 39 import java.net.ServerSocket; 40 import java.net.Socket; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.concurrent.CountDownLatch; 44 import java.util.concurrent.ThreadLocalRandom; 45 import java.util.concurrent.TimeUnit; 46 47 /** 48 * Benchmark socket read/write. 49 * 50 */ 51 @BenchmarkMode(Mode.Throughput) 52 @OutputTimeUnit(TimeUnit.SECONDS) 53 @State(Scope.Thread) 54 public class SocketReadWrite { 55 56 static final InetAddress address = InetAddress.getLoopbackAddress(); 57 public static final int TIMEOUT = 10000; 58 59 static class EchoServer implements Runnable { 60 // EchoServer is implemented to execute the same amount echo threads as benchmarking threads are running 61 62 final ServerSocket ss; 63 final int port; 64 final CountDownLatch startedLatch; 65 final int size; 66 final boolean timeout; 67 List<ServerThread> threads = new ArrayList<>(); 68 volatile boolean isDone = false; 69 70 public EchoServer(CountDownLatch await, int size, boolean timeout) throws IOException { 71 this.size = size; 72 this.timeout = timeout; 73 ss = new ServerSocket(0); 74 port = ss.getLocalPort(); 75 this.startedLatch = await; 76 } 77 78 @Override 79 public void run() { 80 startedLatch.countDown(); 81 while (!isDone) { 82 try { 83 Socket s = ss.accept(); 84 s.setTcpNoDelay(true); 85 if (timeout) { 86 s.setSoTimeout(TIMEOUT); 87 } 88 ServerThread st = new ServerThread(s, size); 89 threads.add(st); 90 new Thread(st).start(); 91 } catch (IOException e) { 92 if (!isDone) { 93 e.printStackTrace(); 94 } 95 } 96 } 97 } 98 99 synchronized void close() throws IOException { 100 if (!isDone) { 101 isDone = true; 102 ss.close(); 103 for (ServerThread st : threads) { 104 st.close(); 105 } 106 } 107 } 108 109 static EchoServer instance = null; 110 111 static synchronized EchoServer startServer(int size, boolean timeout) throws IOException { 112 if (instance == null) { 113 CountDownLatch started = new CountDownLatch(1); 114 EchoServer s = new EchoServer(started, size, timeout); 115 new Thread(s).start(); 116 try { 117 started.await(); // wait until server thread started 118 } catch (InterruptedException e) { 119 e.printStackTrace(); 120 } 121 instance = s; 122 } 123 return instance; 124 } 125 126 static class ServerThread implements Runnable { 127 128 final Socket s; 129 final InputStream in; 130 final OutputStream out; 131 final int size; 132 volatile boolean isDone = false; 133 134 ServerThread(Socket s, int size) throws IOException { 135 this.s = s; 136 this.size = size; 137 in = s.getInputStream(); 138 out = s.getOutputStream(); 139 } 140 141 @Override 142 public void run() { 143 byte[] a = new byte[size]; 144 while (!isDone) { 145 try { 146 readN(a, size, this.in); 147 out.write(a); 148 } catch (IOException e) { 149 if (!isDone) { 150 e.printStackTrace(); 151 } 152 } 153 } 154 } 155 156 public void close() throws IOException { 157 isDone = true; 158 s.close(); 159 } 160 161 } 162 } 163 164 static void readN(byte[] array, int size, InputStream in) throws IOException { 165 int nread = 0; 166 while (size > 0) { 167 int n = in.read(array, nread, size); 168 if (n < 0) throw new RuntimeException(); 169 nread += n; 170 size -= n; 171 } 172 } 173 174 EchoServer server; 175 176 @Param({"1", "1024", "8192", "64000", "128000"}) 177 public int size; 178 179 @Param({"false", "true"}) 180 public boolean timeout; 181 182 Socket s; 183 InputStream in; 184 OutputStream out; 185 byte[] array; 186 187 @Setup 188 public void setup() throws IOException { 189 server = EchoServer.startServer(size, timeout); 190 int port = server.port; 191 s = new Socket(address, port); 192 s.setTcpNoDelay(true); 193 if (timeout) { 194 s.setSoTimeout(TIMEOUT); 195 // 10 seconds times is quite large and never will happen (for microbenchmarking), 196 // but it's required since other paths inside SocketImpl are involved 197 } 198 in = s.getInputStream(); 199 out = s.getOutputStream(); 200 array = new byte[size]; 201 ThreadLocalRandom.current().nextBytes(array); 202 } 203 204 @TearDown 205 public void tearDown() throws IOException { 206 server.close(); 207 s.close(); 208 } 209 210 @Benchmark 211 public void echo() throws IOException { 212 out.write(array); 213 readN(array, size, in); 214 } 215 }