/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package test.amslam; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.SystemClock; import android.os.Bundle; import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.View; import android.widget.TextView; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; public class MainActivity extends Activity implements PongReceiver.PingPongResponseListener { private static final String TAG = "AmSlam"; private static final Class[] sTargets; private static final BlockingQueue sWorkQueue = new ArrayBlockingQueue<>(100); private static Context sAppContext; private static final int[] CONCURRENT_TESTS = {1, 2, 4, 6, 8, 10}; private boolean mAutoRun; private TextView mOutput; private int mTestPhase; private long mBatchStartTime; private int mPendingResponses; private int mBatchRemaining; private int mCurrentTargetIndex; private int mTotalReceived; private long mTotalTime; private long mTotalPingTime; private long mTotalPongTime; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sAppContext = getApplicationContext(); setContentView(R.layout.activity_main); mOutput = findViewById(R.id.output); PongReceiver.addListener(this); findViewById(R.id.run).setOnClickListener(view -> { view.setEnabled(false); mOutput.setText(""); startBatch(); }); mAutoRun = getIntent().getBooleanExtra("autorun", false); if (mAutoRun) { findViewById(R.id.run).performClick(); } } @Override protected void onDestroy() { super.onDestroy(); PongReceiver.removeListener(this); } private void startBatch() { if (mBatchRemaining > 0 || mPendingResponses > 0) { // Still sending, skip return; } mBatchStartTime = SystemClock.uptimeMillis(); mBatchRemaining = 10 * CONCURRENT_TESTS[mTestPhase]; mTotalReceived = 0; mTotalTime = mTotalPingTime = mTotalPongTime = 0; log("Starting test with " + CONCURRENT_TESTS[mTestPhase] + " concurrent requests...\n"); continueSend(); } private Class nextTarget() { Class ret = sTargets[mCurrentTargetIndex]; mCurrentTargetIndex = (mCurrentTargetIndex + 1) % sTargets.length; return ret; } private void continueSend() { while (mPendingResponses < CONCURRENT_TESTS[mTestPhase] && mBatchRemaining > 0) { mPendingResponses++; mBatchRemaining--; Class target = nextTarget(); Intent intent = new Intent(getApplicationContext(), target); try { sWorkQueue.put(intent); } catch (InterruptedException e) { throw new RuntimeException(e); } } } @Override public void onPingPongResponse(long send, long bounce, long recv, String remote) { if (send < mBatchStartTime || mPendingResponses == 0) { Log.e(TAG, "received outdated response??"); Log.e(TAG, "send " + send + ", bounce " + bounce + ", recv " + recv + ", batchStart " + mBatchStartTime + ", remote: " + remote); } mPendingResponses--; mTotalReceived++; continueSend(); mTotalTime += (recv - send); mTotalPingTime += (bounce - send); mTotalPongTime += (recv - bounce); if (mPendingResponses == 0) { long now = SystemClock.uptimeMillis(); log(String.format("Sent %d ping/pongs, %d concurrent.\n" + "Total duration %dms (%dms eff. avg)\n" + "Average message took %dms (%dms + %dms)\n", mTotalReceived, CONCURRENT_TESTS[mTestPhase], (now - mBatchStartTime), (now - mBatchStartTime) / mTotalReceived, mTotalTime / mTotalReceived, mTotalPingTime / mTotalReceived, mTotalPongTime / mTotalReceived)); mTestPhase++; if (mTestPhase < CONCURRENT_TESTS.length) { startBatch(); } else { mTestPhase = 0; log("Finished\n"); findViewById(R.id.run).setEnabled(true); if (mAutoRun) { finish(); } } } } private void log(String text) { mOutput.append(text); Log.d(TAG, text); } static { sTargets = new Class[100]; for (int i = 0; i < sTargets.length; i++) { try { sTargets[i] = Class.forName( String.format("test.amslam.subreceivers.PingReceiver%03d", i)); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } Runnable work = () -> { while (true) { try { Intent intent = sWorkQueue.take(); intent.putExtra("start_time", SystemClock.uptimeMillis()); sAppContext.startService(intent); } catch (InterruptedException e) {} } }; // How many worker threads should we spawn? ¯\_(ツ)_/¯ for (int i = 0; i < 10; i++) { new Thread(work, "Slammer" + i).start(); } } }