reduce result fluctuation for memory and cpu tests

- introduce outlier rejection to ignore data too far from median values.
- also increase repetition for memset as memset shows wider fluctuation than memcpy

bug: 8775068
Change-Id: I78978040e747a182db3cebf311fcf776f2cadf4d
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
index 8f6860f..62b0163 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
@@ -35,11 +35,14 @@
  */
 public class BandwidthTest extends PtsAndroidTestCase {
     private static final String TAG = "BandwidthTest";
-    private static final int REPETITION = 10;
+    private static final int MEMCPY_REPETITION = 10;
+    private static final int MEMSET_REPETITION = 30;
     private static final int REPEAT_IN_EACH_CALL = 100;
     private static final int KB = 1024;
     private static final int MB = 1024 * 1024;
     private static final int MEMSET_CHAR = 0xa5;
+    // reject data outside +/- this value * median
+    private static final double OUTLIER_THRESHOLD = 0.1;
 
     @Override
     protected void setUp() throws Exception {
@@ -153,13 +156,13 @@
     }
 
     private void doRunMemcpy(int bufferSize) {
-        double[] result = new double[REPETITION];
+        double[] result = new double[MEMCPY_REPETITION];
         int repeatInEachCall = REPEAT_IN_EACH_CALL;
         if (bufferSize < (1 * MB)) {
             // too small buffer size finishes too early to give accurate result.
             repeatInEachCall *= (1 * MB / bufferSize);
         }
-        for (int i = 0; i < REPETITION; i++) {
+        for (int i = 0; i < MEMCPY_REPETITION; i++) {
             result[i] = MemoryNative.runMemcpy(bufferSize, repeatInEachCall);
         }
         getReportLog().printArray("memcpy time", result, ResultType.LOWER_BETTER,
@@ -168,7 +171,10 @@
                 (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
         getReportLog().printArray("memcpy throughput", mbps, ResultType.HIGHER_BETTER,
                 ResultUnit.MBPS);
-        Stat.StatResult stat = Stat.getStat(mbps);
+        Stat.StatResult stat = Stat.getStatWithOutlierRejection(mbps, OUTLIER_THRESHOLD);
+        if (stat.mDataCount != result.length) {
+            Log.w(TAG, "rejecting " + (result.length - stat.mDataCount) + " outliers");
+        }
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         Point size = new Point();
         wm.getDefaultDisplay().getSize(size);
@@ -183,13 +189,13 @@
     }
 
     private void doRunMemset(int bufferSize) {
-        double[] result = new double[REPETITION];
+        double[] result = new double[MEMSET_REPETITION];
         int repeatInEachCall = REPEAT_IN_EACH_CALL;
         if (bufferSize < (1 * MB)) {
             // too small buffer size finishes too early to give accurate result.
             repeatInEachCall *= (1 * MB / bufferSize);
         }
-        for (int i = 0; i < REPETITION; i++) {
+        for (int i = 0; i < MEMSET_REPETITION; i++) {
             result[i] = MemoryNative.runMemset(bufferSize, repeatInEachCall, MEMSET_CHAR);
         }
         getReportLog().printArray("memset time", result, ResultType.LOWER_BETTER,
@@ -198,7 +204,10 @@
                 (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
         getReportLog().printArray("memset throughput", mbps, ResultType.HIGHER_BETTER,
                 ResultUnit.MBPS);
-        Stat.StatResult stat = Stat.getStat(mbps);
+        Stat.StatResult stat = Stat.getStatWithOutlierRejection(mbps, OUTLIER_THRESHOLD);
+        if (stat.mDataCount != result.length) {
+            Log.w(TAG, "rejecting " + (result.length - stat.mDataCount) + " outliers");
+        }
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         Point size = new Point();
         wm.getDefaultDisplay().getSize(size);
diff --git a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
index 8b17171..8278618 100644
--- a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
@@ -17,6 +17,7 @@
 package com.android.pts.simplecpu;
 
 import android.cts.util.TimeoutReq;
+import android.util.Log;
 
 import com.android.pts.util.ResultType;
 import com.android.pts.util.ResultUnit;
@@ -35,6 +36,8 @@
     private static final int KB = 1024;
     private static final int MB = 1024 * 1024;
     private static final int NUMBER_REPEAT = 20;
+    // reject data outside +/- this value * median
+    private static final double OUTLIER_THRESHOLD = 0.1;
 
     @Override
     protected void setUp() throws Exception {
@@ -99,7 +102,10 @@
         }
         getReportLog().printArray("sorting time", result, ResultType.LOWER_BETTER,
                 ResultUnit.MS);
-        Stat.StatResult stat = Stat.getStat(result);
+        Stat.StatResult stat = Stat.getStatWithOutlierRejection(result, OUTLIER_THRESHOLD);
+        if (stat.mDataCount != result.length) {
+            Log.w(TAG, "rejecting " + (result.length - stat.mDataCount) + " outliers");
+        }
         getReportLog().printSummary("sorting time", stat.mAverage, ResultType.LOWER_BETTER,
                 ResultUnit.MS);
     }
@@ -118,7 +124,10 @@
         }
         getReportLog().printArray("matrix mutiplication time", result, ResultType.LOWER_BETTER,
                 ResultUnit.MS);
-        Stat.StatResult stat = Stat.getStat(result);
+        Stat.StatResult stat = Stat.getStatWithOutlierRejection(result, OUTLIER_THRESHOLD);
+        if (stat.mDataCount != result.length) {
+            Log.w(TAG, "rejecting " + (result.length - stat.mDataCount) + " outliers");
+        }
         getReportLog().printSummary("matrix mutiplication time", stat.mAverage,
                 ResultType.LOWER_BETTER, ResultUnit.MS);
     }
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java b/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
index d56f59e..5560292 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
@@ -16,6 +16,8 @@
 
 package com.android.pts.util;
 
+import java.util.Arrays;
+
 /**
  * Utilities for doing statistics
  *
@@ -30,11 +32,13 @@
         public double mMin;
         public double mMax;
         public double mStddev;
-        public StatResult(double average, double min, double max, double stddev) {
+        public int mDataCount;
+        public StatResult(double average, double min, double max, double stddev, int dataCount) {
             mAverage = average;
             mMin = min;
             mMax = max;
             mStddev = stddev;
+            mDataCount = dataCount;
         }
     }
 
@@ -60,7 +64,58 @@
         eX2 /= data.length;
         // stddev = sqrt(E[X^2] - (E[X])^2)
         double stddev = Math.sqrt(eX2 - average * average);
-        return new StatResult(average, min, max, stddev);
+        return new StatResult(average, min, max, stddev, data.length);
+    }
+
+    /**
+     * Calculate statistics properties likes average, min, max, and stddev for the given array
+     * while rejecting outlier +/- median * rejectionThreshold.
+     * rejectionThreshold should be bigger than 0.0 and be lowerthan 1.0
+     */
+    public static StatResult getStatWithOutlierRejection(double[] data, double rejectionThreshold) {
+        double[] dataCopied = Arrays.copyOf(data, data.length);
+        Arrays.sort(dataCopied);
+        int medianIndex = dataCopied.length / 2;
+        double median;
+        if (dataCopied.length % 2 == 1) {
+            median = dataCopied[medianIndex];
+        } else {
+            median = (dataCopied[medianIndex - 1] + dataCopied[medianIndex]) / 2.0;
+        }
+        double thresholdMin = median * (1.0 - rejectionThreshold);
+        double thresholdMax = median * (1.0 + rejectionThreshold);
+
+        double average = 0.0;
+        double min = median;
+        double max = median;
+        double eX2 = 0.0; // will become E[X^2]
+        int validDataCounter = 0;
+        for (int i = 0; i < data.length; i++) {
+            if ((data[i] > thresholdMin) && (data[i] < thresholdMax)) {
+                validDataCounter++;
+                average += data[i];
+                eX2 += data[i] * data[i];
+                if (data[i] > max) {
+                    max = data[i];
+                }
+                if (data[i] < min) {
+                    min = data[i];
+                }
+            }
+            //TODO report rejected data
+        }
+        double stddev;
+        if (validDataCounter > 0) {
+            average /= validDataCounter;
+            eX2 /= validDataCounter;
+            // stddev = sqrt(E[X^2] - (E[X])^2)
+            stddev = Math.sqrt(eX2 - average * average);
+        } else { // both median is showing too much diff
+            average = median;
+            stddev = 0; // don't care
+        }
+
+        return new StatResult(average, min, max, stddev, validDataCounter);
     }
 
     /**