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 17package org.conscrypt; 18 19import static org.conscrypt.TestUtils.getProtocols; 20import static org.conscrypt.TestUtils.newTextMessage; 21 22import java.io.OutputStream; 23import java.util.concurrent.ExecutorService; 24import java.util.concurrent.Executors; 25import java.util.concurrent.Future; 26import java.util.concurrent.TimeUnit; 27import java.util.concurrent.atomic.AtomicBoolean; 28import java.util.concurrent.atomic.AtomicLong; 29 30/** 31 * Benchmark for comparing performance of client socket implementations. 32 */ 33public final class ClientSocketBenchmark { 34 /** 35 * Provider for the benchmark configuration 36 */ 37 interface Config { 38 SocketType socketType(); 39 int messageSize(); 40 String cipher(); 41 ChannelType channelType(); 42 } 43 44 private ClientEndpoint client; 45 private ServerEndpoint server; 46 private byte[] message; 47 private ExecutorService executor; 48 private Future<?> sendingFuture; 49 private volatile boolean stopping; 50 51 private static final AtomicLong bytesCounter = new AtomicLong(); 52 private AtomicBoolean recording = new AtomicBoolean(); 53 54 ClientSocketBenchmark(Config config) throws Exception { 55 recording.set(false); 56 57 message = newTextMessage(config.messageSize()); 58 59 // Always use the same server for consistency across the benchmarks. 60 server = SocketType.CONSCRYPT_ENGINE.newServer( 61 ChannelType.CHANNEL, config.messageSize(), getProtocols(), ciphers(config)); 62 63 server.setMessageProcessor(new ServerEndpoint.MessageProcessor() { 64 @Override 65 public void processMessage(byte[] inMessage, int numBytes, OutputStream os) { 66 if (recording.get()) { 67 // Server received a message, increment the count. 68 bytesCounter.addAndGet(numBytes); 69 } 70 } 71 }); 72 Future<?> connectedFuture = server.start(); 73 74 client = config.socketType().newClient( 75 config.channelType(), server.port(), getProtocols(), ciphers(config)); 76 client.start(); 77 78 // Wait for the initial connection to complete. 79 connectedFuture.get(5, TimeUnit.SECONDS); 80 81 executor = Executors.newSingleThreadExecutor(); 82 sendingFuture = executor.submit(new Runnable() { 83 @Override 84 public void run() { 85 Thread thread = Thread.currentThread(); 86 while (!stopping && !thread.isInterrupted()) { 87 client.sendMessage(message); 88 } 89 } 90 }); 91 } 92 93 void close() throws Exception { 94 stopping = true; 95 client.stop(); 96 server.stop(); 97 executor.shutdown(); 98 executor.awaitTermination(5, TimeUnit.SECONDS); 99 sendingFuture.get(5, TimeUnit.SECONDS); 100 } 101 102 /** 103 * Simple benchmark for throughput. 104 */ 105 void throughput() throws Exception { 106 recording.set(true); 107 // Send as many messages as we can in a second. 108 Thread.sleep(1001); 109 recording.set(false); 110 } 111 112 static void reset() { 113 bytesCounter.set(0); 114 } 115 116 static long bytesPerSecond() { 117 return bytesCounter.get(); 118 } 119 120 private String[] ciphers(Config config) { 121 return new String[] {config.cipher()}; 122 } 123 124 /** 125 * A simple main for profiling. 126 */ 127 public static void main(String[] args) throws Exception { 128 ClientSocketBenchmark bm = new ClientSocketBenchmark(new Config() { 129 @Override 130 public SocketType socketType() { 131 return SocketType.CONSCRYPT_ENGINE; 132 } 133 134 @Override 135 public int messageSize() { 136 return 512; 137 } 138 139 @Override 140 public String cipher() { 141 return TestUtils.TEST_CIPHER; 142 } 143 144 @Override 145 public ChannelType channelType() { 146 return ChannelType.CHANNEL; 147 } 148 }); 149 150 // Just run forever for profiling. 151 while (true) { 152 bm.throughput(); 153 } 154 } 155} 156