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    private boolean mDemoMode;
55
56    // In demo mode this is used to count updates in the pipeline.  It's
57    // incremented when work is submitted to RS and decremented when invalidate is
58    // called to display a result.
59
60    // Message processor to handle notifications for when kernel completes
61    private class MessageProcessor extends RenderScript.RSMessageHandler {
62        MessageProcessor() {
63        }
64
65        public void run() {
66            synchronized(mProcessor) {
67                mProcessor.notifyAll();
68            }
69        }
70    }
71
72
73    /////////////////////////////////////////////////////////////////////////
74    // Processor is a helper thread for running the work without
75    // blocking the UI thread.
76    class Processor extends Thread {
77        RenderScript mRS;
78
79        private float mLastResult;
80        private boolean mRun = true;
81        private boolean mDoingBenchmark;
82        private TestBase mTest;
83
84        private boolean mBenchmarkMode;
85
86        void runTest() {
87            mTest.runTest();
88        }
89
90        Processor(RenderScript rs, boolean benchmarkMode) {
91            mRS = rs;
92            mRS.setMessageHandler(new MessageProcessor());
93            mBenchmarkMode = benchmarkMode;
94            start();
95        }
96
97        class Result {
98            float totalTime;
99            int iterations;
100            String testInfo;
101        }
102
103        // Run one loop of kernels for at least the specified minimum time.
104        // The function returns the average time in ms for the test run
105        private Result runBenchmarkLoop(float minTime) {
106            Result r = new Result();
107            long t = java.lang.System.currentTimeMillis();
108
109            r.testInfo = mTest.getTestInfo();
110            do {
111                // Run the kernel
112                mTest.runTest();
113                r.iterations ++;
114                // Send our RS message handler a message so we know when this work has completed
115                mRS.sendMessage(0, null);
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);
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                    // Send our RS message handler a message so we know when this work has completed
203                    mRS.sendMessage(0, null);
204                }
205            }
206
207        }
208
209        public void exit() {
210            mRun = false;
211
212            synchronized(this) {
213                notifyAll();
214            }
215
216            try {
217                this.join();
218            } catch(InterruptedException e) {
219            }
220
221            if (mTest != null) {
222                mTest.destroy();
223                mTest = null;
224            }
225            mRS.destroy();
226            mRS = null;
227        }
228    }
229
230
231    private boolean mDoingBenchmark;
232    public Processor mProcessor;
233
234    TestBase changeTest(BlasTestList.TestName t, boolean setupUI) {
235        TestBase tb = BlasTestList.newTest(t);
236        tb.createBaseTest(this);
237        return tb;
238    }
239
240    TestBase changeTest(int id, boolean setupUI) {
241        BlasTestList.TestName t = BlasTestList.TestName.values()[id];
242        return changeTest(t, setupUI);
243    }
244
245    @Override
246    protected void onCreate(Bundle savedInstanceState) {
247        super.onCreate(savedInstanceState);
248        TextView textView = new TextView(this);
249        textView.setTextSize(20);
250        textView.setText("BLAS BenchMark Running.");
251        setContentView(textView);
252    }
253
254    @Override
255    protected void onPause() {
256        super.onPause();
257        mProcessor.exit();
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
274    void startProcessor() {
275        mProcessor = new Processor(RenderScript.create(this), !mDemoMode);
276        if (mDemoMode) {
277            mProcessor.mTest = changeTest(mTestList[0], true);
278        }
279    }
280
281    @Override
282    protected void onResume() {
283        super.onResume();
284        Intent i = getIntent();
285        mTestList = i.getIntArrayExtra("tests");
286
287        mToggleLong = i.getBooleanExtra("enable long", false);
288        mTogglePause = i.getBooleanExtra("enable pause", false);
289        mDemoMode = i.getBooleanExtra("demo", false);
290
291        mTestResults = new float[mTestList.length];
292        mTestInfo = new String[mTestList.length];
293
294        startProcessor();
295    }
296
297    protected void onDestroy() {
298        super.onDestroy();
299    }
300}
301