1 /* 2 * Copyright (C) 2016 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 test.amslam; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.SystemClock; 23 import android.os.Bundle; 24 import android.text.method.ScrollingMovementMethod; 25 import android.util.Log; 26 import android.view.View; 27 import android.widget.TextView; 28 29 import java.util.Queue; 30 import java.util.concurrent.ArrayBlockingQueue; 31 import java.util.concurrent.BlockingQueue; 32 import java.util.concurrent.ConcurrentLinkedQueue; 33 34 public class MainActivity extends Activity implements PongReceiver.PingPongResponseListener { 35 private static final String TAG = "AmSlam"; 36 37 private static final Class<?>[] sTargets; 38 private static final BlockingQueue<Intent> sWorkQueue = new ArrayBlockingQueue<>(100); 39 private static Context sAppContext; 40 private static final int[] CONCURRENT_TESTS = {1, 2, 4, 6, 8, 10}; 41 42 private boolean mAutoRun; 43 44 private TextView mOutput; 45 46 private int mTestPhase; 47 private long mBatchStartTime; 48 private int mPendingResponses; 49 50 private int mBatchRemaining; 51 private int mCurrentTargetIndex; 52 53 private int mTotalReceived; 54 private long mTotalTime; 55 private long mTotalPingTime; 56 private long mTotalPongTime; 57 58 @Override onCreate(Bundle savedInstanceState)59 protected void onCreate(Bundle savedInstanceState) { 60 super.onCreate(savedInstanceState); 61 sAppContext = getApplicationContext(); 62 setContentView(R.layout.activity_main); 63 mOutput = findViewById(R.id.output); 64 PongReceiver.addListener(this); 65 66 findViewById(R.id.run).setOnClickListener(view -> { 67 view.setEnabled(false); 68 mOutput.setText(""); 69 startBatch(); 70 }); 71 72 mAutoRun = getIntent().getBooleanExtra("autorun", false); 73 if (mAutoRun) { 74 findViewById(R.id.run).performClick(); 75 } 76 } 77 78 @Override onDestroy()79 protected void onDestroy() { 80 super.onDestroy(); 81 PongReceiver.removeListener(this); 82 } 83 startBatch()84 private void startBatch() { 85 if (mBatchRemaining > 0 || mPendingResponses > 0) { 86 // Still sending, skip 87 return; 88 } 89 mBatchStartTime = SystemClock.uptimeMillis(); 90 mBatchRemaining = 10 * CONCURRENT_TESTS[mTestPhase]; 91 mTotalReceived = 0; 92 mTotalTime = mTotalPingTime = mTotalPongTime = 0; 93 log("Starting test with " + CONCURRENT_TESTS[mTestPhase] + " concurrent requests...\n"); 94 continueSend(); 95 } 96 nextTarget()97 private Class<?> nextTarget() { 98 Class<?> ret = sTargets[mCurrentTargetIndex]; 99 mCurrentTargetIndex = (mCurrentTargetIndex + 1) % sTargets.length; 100 return ret; 101 } 102 continueSend()103 private void continueSend() { 104 while (mPendingResponses < CONCURRENT_TESTS[mTestPhase] && mBatchRemaining > 0) { 105 mPendingResponses++; 106 mBatchRemaining--; 107 Class<?> target = nextTarget(); 108 Intent intent = new Intent(getApplicationContext(), target); 109 try { 110 sWorkQueue.put(intent); 111 } catch (InterruptedException e) { 112 throw new RuntimeException(e); 113 } 114 } 115 } 116 117 @Override onPingPongResponse(long send, long bounce, long recv, String remote)118 public void onPingPongResponse(long send, long bounce, long recv, String remote) { 119 if (send < mBatchStartTime || mPendingResponses == 0) { 120 Log.e(TAG, "received outdated response??"); 121 Log.e(TAG, "send " + send + ", bounce " + bounce + ", recv " + recv 122 + ", batchStart " + mBatchStartTime + ", remote: " + remote); 123 } 124 mPendingResponses--; 125 mTotalReceived++; 126 continueSend(); 127 mTotalTime += (recv - send); 128 mTotalPingTime += (bounce - send); 129 mTotalPongTime += (recv - bounce); 130 if (mPendingResponses == 0) { 131 long now = SystemClock.uptimeMillis(); 132 log(String.format("Sent %d ping/pongs, %d concurrent.\n" 133 + "Total duration %dms (%dms eff. avg)\n" 134 + "Average message took %dms (%dms + %dms)\n", 135 mTotalReceived, CONCURRENT_TESTS[mTestPhase], 136 (now - mBatchStartTime), (now - mBatchStartTime) / mTotalReceived, 137 mTotalTime / mTotalReceived, mTotalPingTime / mTotalReceived, 138 mTotalPongTime / mTotalReceived)); 139 140 mTestPhase++; 141 if (mTestPhase < CONCURRENT_TESTS.length) { 142 startBatch(); 143 } else { 144 mTestPhase = 0; 145 log("Finished\n"); 146 findViewById(R.id.run).setEnabled(true); 147 if (mAutoRun) { 148 finish(); 149 } 150 } 151 } 152 } 153 log(String text)154 private void log(String text) { 155 mOutput.append(text); 156 Log.d(TAG, text); 157 } 158 159 static { 160 sTargets = new Class<?>[100]; 161 for (int i = 0; i < sTargets.length; i++) { 162 try { 163 sTargets[i] = Class.forName( 164 String.format("test.amslam.subreceivers.PingReceiver%03d", i)); 165 } catch (ClassNotFoundException e) { 166 throw new RuntimeException(e); 167 } 168 } 169 170 Runnable work = () -> { 171 while (true) { 172 try { 173 Intent intent = sWorkQueue.take(); 174 intent.putExtra("start_time", SystemClock.uptimeMillis()); 175 sAppContext.startService(intent); 176 } catch (InterruptedException e) {} 177 } 178 }; 179 180 // How many worker threads should we spawn? ¯\_(ツ)_/¯ 181 for (int i = 0; i < 10; i++) { 182 new Thread(work, "Slammer" + i).start(); 183 } 184 } 185 } 186