1/*
2 * Copyright (C) 2015 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 com.example.android.rs.blasbenchmark;
18
19import android.app.Activity;
20
21import android.content.Intent;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.Message;
25import android.widget.AdapterView;
26import android.widget.ArrayAdapter;
27import android.widget.TextView;
28import android.view.View;
29import android.graphics.Point;
30import android.view.WindowManager;
31import android.text.method.ScrollingMovementMethod;
32
33import android.util.Log;
34import android.renderscript.ScriptC;
35import android.renderscript.RenderScript;
36import android.renderscript.Type;
37import android.renderscript.Allocation;
38import android.renderscript.Element;
39import android.renderscript.Script;
40
41public class BlasBenchmark extends Activity {
42
43
44    private final String TAG = "BLAS";
45    public final String RESULT_FILE = "blas_benchmark_result.csv";
46
47    private int mTestList[];
48    private float mTestResults[];
49    private String mTestInfo[];
50
51    private TextView mTextView;
52    private boolean mToggleLong;
53    private boolean mTogglePause;
54
55    // In demo mode this is used to count updates in the pipeline.  It's
56    // incremented when work is submitted to RS and decremented when invalidate is
57    // called to display a result.
58    private boolean mDemoMode;
59
60    // Initialize the parameters for Instrumentation tests.
61    protected void prepareInstrumentationTest() {
62        mTestList = new int[1];
63        mTestResults = new float[1];
64        mTestInfo = new String[1];
65        mDemoMode = false;
66        mProcessor = new Processor(RenderScript.create(this), !mDemoMode);
67    }
68
69
70    /////////////////////////////////////////////////////////////////////////
71    // Processor is a helper thread for running the work without
72    // blocking the UI thread.
73    class Processor extends Thread {
74        RenderScript mRS;
75
76        private float mLastResult;
77        private boolean mRun = true;
78        private boolean mDoingBenchmark;
79        private TestBase mTest;
80
81        private boolean mBenchmarkMode;
82
83        void runTest() {
84            mTest.runTest();
85        }
86
87        Processor(RenderScript rs, boolean benchmarkMode) {
88            mRS = rs;
89            mBenchmarkMode = benchmarkMode;
90        }
91
92        class Result {
93            float totalTime;
94            int iterations;
95            String testInfo;
96        }
97
98        // Method to retreive benchmark results for instrumentation tests.
99        float getInstrumentationResult(BlasTestList.TestName t) {
100            mTest = changeTest(t, false);
101            Result r = getBenchmark();
102            return r.totalTime / r.iterations * 1000.f;
103        }
104
105        // Run one loop of kernels for at least the specified minimum time.
106        // The function returns the average time in ms for the test run
107        private Result runBenchmarkLoop(float minTime) {
108            Result r = new Result();
109            long t = java.lang.System.currentTimeMillis();
110
111            r.testInfo = mTest.getTestInfo();
112            do {
113                // Run the kernel
114                mTest.runTest();
115                r.iterations ++;
116
117                long t2 = java.lang.System.currentTimeMillis();
118                r.totalTime += (t2 - t) / 1000.f;
119                t = t2;
120            } while (r.totalTime < minTime);
121
122            // Wait for any stray operations to complete and update the final time
123            mRS.finish();
124            long t2 = java.lang.System.currentTimeMillis();
125            r.totalTime += (t2 - t) / 1000.f;
126            t = t2;
127            return r;
128        }
129
130
131        // Get a benchmark result for a specific test
132        private Result getBenchmark() {
133            mDoingBenchmark = true;
134
135            long result = 0;
136            float runtime = 1.f;
137            if (mToggleLong) {
138                runtime = 10.f;
139            }
140
141            // We run a short bit of work before starting the actual test
142            // this is to let any power management do its job and respond
143            runBenchmarkLoop(0.3f);
144
145            // Run the actual benchmark
146            Result r = runBenchmarkLoop(runtime);
147
148            Log.v("rs", "Test: time=" + r.totalTime +"s,  iterations=" + r.iterations +
149                  ", avg=" + r.totalTime / r.iterations * 1000.f + " " + r.testInfo);
150
151            mDoingBenchmark = false;
152            return r;
153        }
154
155        public void run() {
156            while (mRun) {
157                // Our loop for launching tests or benchmarks
158                synchronized(this) {
159                    // We may have been asked to exit while waiting
160                    if (!mRun) return;
161                }
162
163                if (mBenchmarkMode) {
164                    // Loop over the tests we want to benchmark
165                    for (int ct=0; (ct < mTestList.length) && mRun; ct++) {
166
167                        // For reproducibility we wait a short time for any sporadic work
168                        // created by the user touching the screen to launch the test to pass.
169                        // Also allows for things to settle after the test changes.
170                        mRS.finish();
171                        try {
172                            sleep(250);
173                        } catch(InterruptedException e) {
174                        }
175
176                        // If we just ran a test, we destroy it here to relieve some memory pressure
177                        if (mTest != null) {
178                            mTest.destroy();
179                        }
180
181                        // Select the next test
182                        mTest = changeTest(mTestList[ct], false);
183                        // If the user selected the "long pause" option, wait
184                        if (mTogglePause) {
185                            for (int i=0; (i < 100) && mRun; i++) {
186                                try {
187                                    sleep(100);
188                                } catch(InterruptedException e) {
189                                }
190                            }
191                        }
192
193                        // Run the test
194                        Result r = getBenchmark();
195                        mTestResults[ct] = r.totalTime / r.iterations * 1000.f;
196                        mTestInfo[ct] = r.testInfo;
197                    }
198                    onBenchmarkFinish(mRun);
199                } else {
200                    // Run the kernel
201                    runTest();
202                }
203            }
204
205        }
206
207        public void exit() {
208            mRun = false;
209
210            synchronized(this) {
211                notifyAll();
212            }
213
214            try {
215                this.join();
216            } catch(InterruptedException e) {
217            }
218
219            if (mTest != null) {
220                mTest.destroy();
221                mTest = null;
222            }
223            mRS.destroy();
224            mRS = null;
225        }
226    }
227
228
229    private boolean mDoingBenchmark;
230    public Processor mProcessor;
231
232    TestBase changeTest(BlasTestList.TestName t, boolean setupUI) {
233        TestBase tb = BlasTestList.newTest(t);
234        tb.createBaseTest(this);
235        return tb;
236    }
237
238    TestBase changeTest(int id, boolean setupUI) {
239        BlasTestList.TestName t = BlasTestList.TestName.values()[id];
240        return changeTest(t, setupUI);
241    }
242
243    @Override
244    protected void onCreate(Bundle savedInstanceState) {
245        super.onCreate(savedInstanceState);
246        TextView textView = new TextView(this);
247        textView.setTextSize(20);
248        textView.setText("BLAS BenchMark Running.");
249        setContentView(textView);
250    }
251
252    @Override
253    protected void onPause() {
254        super.onPause();
255        if (mProcessor != null) {
256            mProcessor.exit();
257        }
258    }
259
260    public void onBenchmarkFinish(boolean ok) {
261        if (ok) {
262            Intent intent = new Intent();
263            intent.putExtra("tests", mTestList);
264            intent.putExtra("results", mTestResults);
265            intent.putExtra("testinfo", mTestInfo);
266            setResult(RESULT_OK, intent);
267        } else {
268            setResult(RESULT_CANCELED);
269        }
270        finish();
271    }
272
273    @Override
274    protected void onResume() {
275        super.onResume();
276        Intent i = getIntent();
277        mTestList = i.getIntArrayExtra("tests");
278
279        mToggleLong = i.getBooleanExtra("enable long", false);
280        mTogglePause = i.getBooleanExtra("enable pause", false);
281        mDemoMode = i.getBooleanExtra("demo", false);
282
283        if (mTestList != null) {
284            mTestResults = new float[mTestList.length];
285            mTestInfo = new String[mTestList.length];
286            mProcessor = new Processor(RenderScript.create(this), !mDemoMode);
287            if (mDemoMode) {
288                mProcessor.mTest = changeTest(mTestList[0], true);
289            }
290            mProcessor.start();
291        }
292    }
293
294    protected void onDestroy() {
295        super.onDestroy();
296    }
297}
298