1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import static org.conscrypt.TestUtils.getProtocols; 20 import static org.conscrypt.TestUtils.newTextMessage; 21 22 import java.io.OutputStream; 23 import java.util.concurrent.ExecutorService; 24 import java.util.concurrent.Executors; 25 import java.util.concurrent.Future; 26 import java.util.concurrent.TimeUnit; 27 import java.util.concurrent.atomic.AtomicBoolean; 28 import java.util.concurrent.atomic.AtomicLong; 29 30 /** 31 * Benchmark for comparing performance of client socket implementations. 32 */ 33 public final class ClientSocketBenchmark { 34 /** 35 * Provider for the benchmark configuration 36 */ 37 interface Config { 38 EndpointFactory clientFactory(); 39 EndpointFactory serverFactory(); 40 int messageSize(); 41 String cipher(); 42 ChannelType channelType(); 43 } 44 45 private ClientEndpoint client; 46 private ServerEndpoint server; 47 private byte[] message; 48 private ExecutorService executor; 49 private Future<?> sendingFuture; 50 private volatile boolean stopping; 51 52 private static final AtomicLong bytesCounter = new AtomicLong(); 53 private AtomicBoolean recording = new AtomicBoolean(); 54 55 ClientSocketBenchmark(Config config) throws Exception { 56 recording.set(false); 57 58 message = newTextMessage(config.messageSize()); 59 60 // Always use the same server for consistency across the benchmarks. 61 server = config.serverFactory().newServer( 62 ChannelType.CHANNEL, config.messageSize(), getProtocols(), ciphers(config)); 63 64 server.setMessageProcessor(new ServerEndpoint.MessageProcessor() { 65 @Override 66 public void processMessage(byte[] inMessage, int numBytes, OutputStream os) { 67 if (recording.get()) { 68 // Server received a message, increment the count. 69 bytesCounter.addAndGet(numBytes); 70 } 71 } 72 }); 73 Future<?> connectedFuture = server.start(); 74 75 client = config.clientFactory().newClient( 76 config.channelType(), server.port(), getProtocols(), ciphers(config)); 77 client.start(); 78 79 // Wait for the initial connection to complete. 80 connectedFuture.get(5, TimeUnit.SECONDS); 81 82 executor = Executors.newSingleThreadExecutor(); 83 sendingFuture = executor.submit(new Runnable() { 84 @Override 85 public void run() { 86 try { 87 Thread thread = Thread.currentThread(); 88 while (!stopping && !thread.isInterrupted()) { 89 client.sendMessage(message); 90 } 91 } finally { 92 client.flush(); 93 } 94 } 95 }); 96 } 97 98 void close() throws Exception { 99 stopping = true; 100 101 // Wait for the sending thread to stop. 102 sendingFuture.get(5, TimeUnit.SECONDS); 103 104 client.stop(); 105 server.stop(); 106 executor.shutdown(); 107 executor.awaitTermination(5, TimeUnit.SECONDS); 108 } 109 110 /** 111 * Simple benchmark for the amount of time to send a given number of messages (used by 112 * Caliper). 113 */ 114 void time(final int numMessages) throws Exception { 115 reset(); 116 recording.set(true); 117 118 while (bytesCounter.get() < numMessages) { 119 Thread.sleep(50); 120 } 121 122 recording.set(false); 123 } 124 125 /** 126 * Simple benchmark for throughput (used by JMH). 127 */ 128 void throughput() throws Exception { 129 recording.set(true); 130 // Send as many messages as we can in a second. 131 Thread.sleep(1001); 132 recording.set(false); 133 } 134 135 static void reset() { 136 bytesCounter.set(0); 137 } 138 139 static long bytesPerSecond() { 140 return bytesCounter.get(); 141 } 142 143 private String[] ciphers(Config config) { 144 return new String[] {config.cipher()}; 145 } 146 } 147