• 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     https://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 org.tensorflow.ovic;
16 
17 import android.graphics.Bitmap;
18 import android.os.SystemClock;
19 import android.util.Log;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.nio.ByteBuffer;
23 import java.nio.MappedByteBuffer;
24 
25 /**
26  * Base class that benchmarks image models.
27  *
28  * <p>===================== General workflow =======================
29  *
30  * <pre>{@code
31  * benchmarker = new OvicBenchmarker();
32  * benchmarker.getReadyToTest(labelInputStream, model);
33  * while (!benchmarker.shouldStop()) {
34  *   Bitmap bitmap = ...
35  *   imgId = ...
36  *   benchmarker.processBitmap(bitmap, imgId);
37  * }
38  * }</pre>
39  */
40 public abstract class OvicBenchmarker {
41   /** Tag for the {@link Log}. */
42   private static final String TAG = "OvicBenchmarker";
43 
44   /** Dimensions of inputs. */
45   protected static final int DIM_BATCH_SIZE = 1;
46 
47   protected static final int DIM_PIXEL_SIZE = 3;
48   protected int imgHeight = 224;
49   protected int imgWidth = 224;
50 
51   /* Preallocated buffers for storing image data in. */
52   protected int[] intValues = null;
53 
54   /** A ByteBuffer to hold image data, to be feed into classifier as inputs. */
55   protected ByteBuffer imgData = null;
56 
57   /** Total runtime in ns. */
58   protected double totalRuntimeNano = 0.0;
59   /** Total allowed runtime in ms. */
60   protected double wallTimeMilli = 20000 * 30.0;
61   /** Record whether benchmark has started (used to skip the first image). */
62   protected boolean benchmarkStarted = false;
63 
64   /**
65    * Initializes an {@link OvicBenchmarker}
66    *
67    * @param wallTimeMilli: a double number specifying the total amount of time to benchmark.
68    */
OvicBenchmarker(double wallTimeMilli)69   protected OvicBenchmarker(double wallTimeMilli) {
70     benchmarkStarted = false;
71     totalRuntimeNano = 0.0;
72     this.wallTimeMilli = wallTimeMilli;
73   }
74 
75   /** Return the cumulative latency of all runs so far. */
getTotalRuntimeNano()76   public double getTotalRuntimeNano() {
77     return totalRuntimeNano;
78   }
79 
80   /** Check whether the benchmarker should stop. */
shouldStop()81   public Boolean shouldStop() {
82     if ((totalRuntimeNano * 1.0 / 1e6) >= wallTimeMilli) {
83       Log.e(
84           TAG,
85           "Total runtime "
86               + (totalRuntimeNano * 1.0 / 1e6)
87               + " exceeded walltime (ms) "
88               + wallTimeMilli);
89       return true;
90     }
91     return false;
92   }
93 
94   /** Abstract class for checking whether the benchmarker is ready to start processing images */
readyToTest()95   public abstract boolean readyToTest();
96 
97   /**
98    * Abstract class for getting the benchmarker ready.
99    *
100    * @param labelInputStream: an {@link InputStream} specifying where the list of labels should be
101    *     read from.
102    * @param model: a {@link MappedByteBuffer} model to benchmark.
103    */
getReadyToTest(InputStream labelInputStream, MappedByteBuffer model)104   public abstract void getReadyToTest(InputStream labelInputStream, MappedByteBuffer model);
105 
106   /**
107    * Perform test on a single bitmap image.
108    *
109    * @param bitmap: a {@link Bitmap} image to process.
110    * @param imageId: an ID uniquely representing the image.
111    */
processBitmap(Bitmap bitmap, int imageId)112   public abstract boolean processBitmap(Bitmap bitmap, int imageId)
113       throws IOException, InterruptedException;
114 
115   /** Perform test on a single bitmap image without an image ID. */
processBitmap(Bitmap bitmap)116   public boolean processBitmap(Bitmap bitmap) throws IOException, InterruptedException {
117     return processBitmap(bitmap, /* imageId = */ 0);
118   }
119 
120   /** Returns the last inference results as string. */
getLastResultString()121   public abstract String getLastResultString();
122 
123   /**
124    * Loads input buffer from intValues into ByteBuffer for the interpreter. Input buffer must be
125    * loaded in intValues and output will be placed in imgData.
126    */
loadsInputToByteBuffer()127   protected void loadsInputToByteBuffer() {
128     if (imgData == null || intValues == null) {
129       throw new RuntimeException("Benchmarker is not yet ready to test.");
130     }
131     // Convert the image to ByteBuffer.
132     imgData.rewind();
133     int pixel = 0;
134     long startTime = SystemClock.uptimeMillis();
135 
136     for (int i = 0; i < imgHeight; ++i) {
137       for (int j = 0; j < imgWidth; ++j) {
138         final int pixelValue = intValues[pixel++];
139         imgData.put((byte) ((pixelValue >> 16) & 0xFF));
140         imgData.put((byte) ((pixelValue >> 8) & 0xFF));
141         imgData.put((byte) (pixelValue & 0xFF));
142       }
143     }
144     long endTime = SystemClock.uptimeMillis();
145     Log.d(TAG, "Timecost to put values into ByteBuffer: " + Long.toString(endTime - startTime));
146   }
147 }
148