• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 package ovic.demo.app;
16 
17 import android.app.Activity;
18 import android.content.res.AssetFileDescriptor;
19 import android.content.res.AssetManager;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.os.Bundle;
23 import android.os.Process;
24 import android.os.SystemClock;
25 import android.util.Log;
26 import android.view.View;
27 import android.widget.TextView;
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileReader;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.nio.MappedByteBuffer;
35 import java.nio.channels.FileChannel;
36 import java.text.DecimalFormat;
37 import org.tensorflow.ovic.OvicBenchmarker;
38 import org.tensorflow.ovic.OvicClassifierBenchmarker;
39 import org.tensorflow.ovic.OvicDetectorBenchmarker;
40 
41 /** Class that benchmark image classifier models. */
42 public class OvicBenchmarkerActivity extends Activity {
43   /** Tag for the {@link Log}. */
44   private static final String TAG = "OvicBenchmarkerActivity";
45 
46   /** Name of the task-dependent data files stored in Assets. */
47   private static String labelPath = null;
48   private static String testImagePath = null;
49   private static String modelPath = null;
50   /**
51    * Each bottom press will launch a benchmarking experiment. The experiment stops when either the
52    * total native latency reaches WALL_TIME or the number of iterations reaches MAX_ITERATIONS,
53    * whichever comes first.
54    */
55   /** Wall time for each benchmarking experiment. */
56   private static final double WALL_TIME = 3000;
57   /** Maximum number of iterations in each benchmarking experiment. */
58   private static final int MAX_ITERATIONS = 100;
59   /** Mask for binding to a single big core. Pixel 1 (4), Pixel 2 (16). */
60   private static final int BIG_CORE_MASK = 16;
61   /** Amount of time in milliseconds to wait for affinity to set. */
62   private static final int WAIT_TIME_FOR_AFFINITY = 1000;
63 
64   /* The model to be benchmarked. */
65   private MappedByteBuffer model = null;
66   private InputStream labelInputStream = null;
67   private OvicBenchmarker benchmarker;
68 
69   private TextView textView = null;
70   // private Button startButton = null;
71   private static final DecimalFormat df2 = new DecimalFormat(".##");
72 
73   @Override
onCreate(Bundle savedInstanceState)74   protected void onCreate(Bundle savedInstanceState) {
75     super.onCreate(savedInstanceState);
76     setContentView(R.layout.activity_main);
77 
78     // TextView used to display the progress, for information purposes only.
79     textView = (TextView) findViewById(R.id.textView);
80   }
81 
loadTestBitmap()82   private Bitmap loadTestBitmap() throws IOException {
83     InputStream imageStream = getAssets().open(testImagePath);
84     return BitmapFactory.decodeStream(imageStream);
85   }
86 
initializeTest(boolean benchmarkClassification)87   public void initializeTest(boolean benchmarkClassification) throws IOException {
88     Log.i(TAG, "Initializing benchmarker.");
89     if (benchmarkClassification) {
90       benchmarker = new OvicClassifierBenchmarker(WALL_TIME);
91       labelPath = "labels.txt";
92       testImagePath = "test_image_224.jpg";
93       modelPath = "quantized_model.lite";
94     } else {  // Benchmarking detection.
95       benchmarker = new OvicDetectorBenchmarker(WALL_TIME);
96       labelPath = "coco_labels.txt";
97       testImagePath = "test_image_224.jpg";
98       modelPath = "detect.lite";
99     }
100     AssetManager am = getAssets();
101     AssetFileDescriptor fileDescriptor = am.openFd(modelPath);
102     FileInputStream modelInputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
103     FileChannel fileChannel = modelInputStream.getChannel();
104     long startOffset = fileDescriptor.getStartOffset();
105     long declaredLength = fileDescriptor.getDeclaredLength();
106     model = fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
107     labelInputStream = am.open(labelPath);
108   }
109 
doTestIteration()110   public Boolean doTestIteration() throws IOException, InterruptedException {
111     if (benchmarker == null) {
112       throw new RuntimeException("Benchmarker has not been initialized.");
113     }
114     if (benchmarker.shouldStop()) {
115       return false;
116     }
117     if (!benchmarker.readyToTest()) {
118       Log.i(TAG, "getting ready to test.");
119       benchmarker.getReadyToTest(labelInputStream, model);
120       if (!benchmarker.readyToTest()) {
121         throw new RuntimeException("Failed to get the benchmarker ready.");
122       }
123     }
124     Log.i(TAG, "Going to do test iter.");
125     // Start testing.
126     Bitmap testImageBitmap = loadTestBitmap();
127     try {
128       if (!benchmarker.processBitmap(testImageBitmap)) {
129         throw new RuntimeException("Failed to run test.");
130       }
131     } catch (Exception e) {
132       e.printStackTrace();
133       throw e;
134     } finally {
135       testImageBitmap.recycle();
136     }
137     String iterResultString = benchmarker.getLastResultString();
138     if (iterResultString == null) {
139       throw new RuntimeException("Inference failed to produce a result.");
140     }
141     Log.i(TAG, iterResultString);
142     return true;
143   }
144 
detectPressed(View view)145   public void detectPressed(View view) throws IOException {
146     benchmarkSession(false);
147   }
classifyPressed(View view)148   public void classifyPressed(View view) throws IOException {
149     benchmarkSession(true);
150   }
151 
benchmarkSession(boolean benchmarkClassification)152   private void benchmarkSession(boolean benchmarkClassification) throws IOException {
153     try {
154       initializeTest(benchmarkClassification);
155     } catch (IOException e) {
156       Log.e(TAG, "Can't initialize benchmarker.", e);
157       throw e;
158     }
159     String displayText = "";
160     if (benchmarkClassification) {
161       displayText = "Classification benchmark: ";
162     } else {
163       displayText = "Detection benchmark: ";
164     }
165     try {
166       setProcessorAffinity(BIG_CORE_MASK);
167     } catch (IOException e) {
168       Log.e(TAG, e.getMessage());
169       displayText = e.getMessage() + "\n";
170     }
171     Log.i(TAG, "Successfully initialized benchmarker.");
172     int testIter = 0;
173     Boolean iterSuccess = false;
174     while (testIter < MAX_ITERATIONS) {
175       try {
176         iterSuccess = doTestIteration();
177       } catch (IOException e) {
178         Log.e(TAG, "Error during iteration " + testIter);
179         throw e;
180       } catch (InterruptedException e) {
181         Log.e(TAG, "Interrupted at iteration " + testIter);
182         displayText += e.getMessage() + "\n";
183       }
184       if (!iterSuccess) {
185         break;
186       }
187       testIter++;
188     }
189     Log.i(TAG, "Benchmarking finished");
190 
191     if (textView != null) {
192       if (testIter > 0) {
193         textView.setText(
194             displayText
195                 + modelPath
196                 + ": Average latency="
197                 + df2.format(benchmarker.getTotalRunTime() / testIter)
198                 + "ms after "
199                 + testIter
200                 + " runs.");
201       } else {
202         textView.setText("Benchmarker failed to run on more than one images.");
203       }
204     }
205   }
206 
setProcessorAffinity(int mask)207   private static void setProcessorAffinity(int mask) throws IOException {
208     int myPid = Process.myPid();
209     Log.i(TAG, String.format("Setting processor affinity to 0x%02x", mask));
210 
211     String command = String.format("taskset -a -p %x %d", mask, myPid);
212     try {
213       Runtime.getRuntime().exec(command).waitFor();
214     } catch (InterruptedException e) {
215       throw new IOException("Interrupted: " + e);
216     }
217 
218     // Make sure set took effect - try for a second to confirm the change took.  If not then fail.
219     long startTimeMs = SystemClock.elapsedRealtime();
220     while (true) {
221       int readBackMask = readCpusAllowedMask();
222       if (readBackMask == mask) {
223         Log.i(TAG, String.format("Successfully set affinity to 0x%02x", mask));
224         return;
225       }
226       if (SystemClock.elapsedRealtime() > startTimeMs + WAIT_TIME_FOR_AFFINITY) {
227         throw new IOException(
228             String.format(
229                 "Core-binding failed: affinity set to 0x%02x but read back as 0x%02x\n"
230                     + "please root device.",
231                 mask, readBackMask));
232       }
233 
234       try {
235         Thread.sleep(50);
236       } catch (InterruptedException e) {
237         // Ignore sleep interrupted, will sleep again and compare is final cross-check.
238       }
239     }
240   }
241 
readCpusAllowedMask()242   public static int readCpusAllowedMask() throws IOException {
243     // Determine how many CPUs there are total
244     final String pathname = "/proc/self/status";
245     final String resultPrefix = "Cpus_allowed:";
246     File file = new File(pathname);
247     String line = "<NO LINE READ>";
248     String allowedCPU = "";
249     Integer allowedMask = null;
250     BufferedReader bufReader = null;
251     try {
252       bufReader = new BufferedReader(new FileReader(file));
253       while ((line = bufReader.readLine()) != null) {
254         if (line.startsWith(resultPrefix)) {
255           allowedMask = Integer.valueOf(line.substring(resultPrefix.length()).trim(), 16);
256           allowedCPU = bufReader.readLine();
257           break;
258         }
259       }
260     } catch (RuntimeException e) {
261       throw new IOException(
262           "Invalid number in " + pathname + " line: \"" + line + "\": " + e.getMessage());
263     } finally {
264       if (bufReader != null) {
265         bufReader.close();
266       }
267     }
268     if (allowedMask == null) {
269       throw new IOException(pathname + " missing " + resultPrefix + " line");
270     }
271     Log.i(TAG, allowedCPU);
272     return allowedMask;
273   }
274 }
275