blob: 02ca8ab1732d857ccc2af38897905f70ae9a3c53 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* UT_reduce_backward.java is a much simpler version of this test
* case that exercises pragmas after the functions (backward
* reference), whereas this test case exercises the pragmas before
* the functions (forward reference).
*/
package com.android.rs.test;
import android.content.Context;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.Float2;
import android.renderscript.Int2;
import android.renderscript.Int3;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicHistogram;
import android.renderscript.Type;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
public class UT_reduce extends UnitTest {
private static final String TAG = "reduce";
protected UT_reduce(RSTestCore rstc, Context ctx) {
super(rstc, "reduce", ctx);
}
private static class timing {
timing(long myJavaStart, long myJavaEnd, long myRsStart,
long myCopyStart, long myKernelStart, long myRsEnd,
Allocation... myInputs) {
javaStart = myJavaStart;
javaEnd = myJavaEnd;
rsStart = myRsStart;
copyStart = myCopyStart;
kernelStart = myKernelStart;
rsEnd = myRsEnd;
inputBytes = 0;
for (Allocation input : myInputs)
inputBytes += input.getBytesSize();
inputCells = (myInputs.length > 0) ? myInputs[0].getType().getCount() : 0;
}
timing(long myInputCells) {
inputCells = myInputCells;
}
private long javaStart = -1;
private long javaEnd = -1;
private long rsStart = -1;
private long copyStart = -1;
private long kernelStart = -1;
private long rsEnd = -1;
private long inputBytes = -1;
private long inputCells = -1;
public long javaTime() {
return javaEnd - javaStart;
}
public long rsTime() {
return rsEnd - rsStart;
}
public long kernelTime() {
return rsEnd - kernelStart;
}
public long overheadTime() {
return kernelStart - rsStart;
}
public long allocationTime() {
return copyStart - rsStart;
}
public long copyTime() {
return kernelStart - copyStart;
}
public static String string(long myJavaStart, long myJavaEnd, long myRsStart,
long myCopyStart, long myKernelStart, long myRsEnd,
Allocation... myInputs) {
return (new timing(myJavaStart, myJavaEnd, myRsStart,
myCopyStart, myKernelStart, myRsEnd, myInputs)).string();
}
public static String string(long myInputCells) {
return (new timing(myInputCells)).string();
}
public String string() {
String result;
if (javaStart >= 0) {
result = "(java " + javaTime() + "ms, rs " + rsTime() + "ms = overhead " +
overheadTime() + "ms (alloc " + allocationTime() + "ms + copy " +
copyTime() + "ms) + kernel+get() " + kernelTime() + "ms)";
if (inputCells > 0)
result += " ";
} else {
result = "";
}
if (inputCells > 0) {
result += "(" + fmt.format(inputCells) + " cells";
if (inputBytes > 0)
result += ", " + fmt.format(inputBytes) + " bytes";
result += ")";
}
return result;
}
private static java.text.DecimalFormat fmt;
static {
fmt = new java.text.DecimalFormat("###,###");
}
}
private byte[] createInputArrayByte(int len, int seed) {
byte[] array = new byte[len];
(new Random(seed)).nextBytes(array);
return array;
}
private float[] createInputArrayFloat(int len, Random rand) {
float[] array = new float[len];
for (int i = 0; i < len; ++i) {
final float val = rand.nextFloat();
array[i] = rand.nextBoolean() ? val : -val;
}
return array;
}
private float[] createInputArrayFloat(int len, int seed) {
return createInputArrayFloat(len, new Random(seed));
}
private float[] createInputArrayFloatWithInfs(int len, int infs, int seed) {
Random rand = new Random(seed);
float[] array = createInputArrayFloat(len, rand);
for (int i = 0; i < infs; ++i)
array[rand.nextInt(len)] = (rand.nextBoolean() ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY);
return array;
}
private int[] createInputArrayInt(int len, int seed) {
Random rand = new Random(seed);
int[] array = new int[len];
for (int i = 0; i < len; ++i)
array[i] = rand.nextInt();
return array;
}
private int[] createInputArrayInt(int len, int seed, int eltRange) {
Random rand = new Random(seed);
int[] array = new int[len];
for (int i = 0; i < len; ++i)
array[i] = rand.nextInt(eltRange);
return array;
}
private long[] intArrayToLong(final int[] input) {
final long[] output = new long[input.length];
for (int i = 0; i < input.length; ++i)
output[i] = input[i];
return output;
}
private <T extends Number> boolean result(String testName, final timing t,
T javaResult, T rsResult) {
final boolean success = javaResult.equals(rsResult);
String status = (success ? "PASSED" : "FAILED");
if (success && (t != null))
status += " " + t.string();
Log.i(TAG, testName + ": java " + javaResult + ", rs " + rsResult + ": " + status);
return success;
}
private boolean result(String testName, final timing t,
final float[] javaResult, final float[] rsResult) {
if (javaResult.length != rsResult.length) {
Log.i(TAG, testName + ": java length " + javaResult.length +
", rs length " + rsResult.length + ": FAILED");
return false;
}
for (int i = 0; i < javaResult.length; ++i) {
if (javaResult[i] != rsResult[i]) {
Log.i(TAG, testName + "[" + i + "]: java " + javaResult[i] +
", rs " + rsResult[i] + ": FAILED");
return false;
}
}
String status = "PASSED";
if (t != null)
status += " " + t.string();
Log.i(TAG, testName + ": " + status);
return true;
}
private boolean result(String testName, final timing t,
final long[] javaResult, final long[] rsResult) {
if (javaResult.length != rsResult.length) {
Log.i(TAG, testName + ": java length " + javaResult.length +
", rs length " + rsResult.length + ": FAILED");
return false;
}
for (int i = 0; i < javaResult.length; ++i) {
if (javaResult[i] != rsResult[i]) {
Log.i(TAG, testName + "[" + i + "]: java " + javaResult[i] +
", rs " + rsResult[i] + ": FAILED");
return false;
}
}
String status = "PASSED";
if (t != null)
status += " " + t.string();
Log.i(TAG, testName + ": " + status);
return true;
}
private boolean result(String testName, final timing t,
final int[] javaResult, final int[] rsResult) {
return result(testName, t, intArrayToLong(javaResult), intArrayToLong(rsResult));
}
private boolean result(String testName, final timing t, Int2 javaResult, Int2 rsResult) {
final boolean success = (javaResult.x == rsResult.x) && (javaResult.y == rsResult.y);
String status = (success ? "PASSED" : "FAILED");
if (success && (t != null))
status += " " + t.string();
Log.i(TAG,
testName +
": java (" + javaResult.x + ", " + javaResult.y + ")" +
", rs (" + rsResult.x + ", " + rsResult.y + ")" +
": " + status);
return success;
}
private boolean result(String testName, final timing t, Float2 javaResult, Float2 rsResult) {
final boolean success = (javaResult.x == rsResult.x) && (javaResult.y == rsResult.y);
String status = (success ? "PASSED" : "FAILED");
if (success && (t != null))
status += " " + t.string();
Log.i(TAG,
testName +
": java (" + javaResult.x + ", " + javaResult.y + ")" +
", rs (" + rsResult.x + ", " + rsResult.y + ")" +
": " + status);
return success;
}
///////////////////////////////////////////////////////////////////
private int addint(int[] input) {
int result = 0;
for (int idx = 0; idx < input.length; ++idx)
result += input[idx];
return result;
}
private boolean addint1D_array(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final int[] input = createInputArrayInt(size[0], seed, Integer.MAX_VALUE / size[0]);
final int javaResult = addint(input);
final int rsResult = s.reduce_addint(input).get();
return result("addint1D_array", new timing(size[0]), javaResult, rsResult);
}
private boolean addint1D(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final int[] inputArray = createInputArrayInt(size[0], seed, Integer.MAX_VALUE / size[0]);
final long javaTimeStart = java.lang.System.currentTimeMillis();
final int javaResult = addint(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocation = Allocation.createSized(RS, Element.I32(RS), inputArray.length);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copyFrom(inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final int rsResult = s.reduce_addint(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final boolean success =
result("addint1D",
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
javaResult, rsResult);
inputAllocation.destroy();
return success;
}
private boolean addint2D(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final int dimX = size[0];
final int dimY = size[1];
final int[] inputArray = createInputArrayInt(dimX * dimY, seed, Integer.MAX_VALUE / (dimX * dimY));
final long javaTimeStart = java.lang.System.currentTimeMillis();
final int javaResult = addint(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
typeBuilder.setX(dimX).setY(dimY);
Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copy2DRangeFrom(0, 0, dimX, dimY, inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final int rsResult = s.reduce_addint(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final boolean success =
result("addint2D",
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
javaResult, rsResult);
inputAllocation.destroy();
return success;
}
private boolean addint3D(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final int dimX = size[0];
final int dimY = size[1];
final int dimZ = size[2];
final int[] inputArray = createInputArrayInt(dimX * dimY * dimZ, seed, Integer.MAX_VALUE / (dimX * dimY * dimZ));
final long javaTimeStart = java.lang.System.currentTimeMillis();
final int javaResult = addint(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
typeBuilder.setX(dimX).setY(dimY).setZ(dimZ);
Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copy3DRangeFrom(0, 0, 0, dimX, dimY, dimZ, inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final int rsResult = s.reduce_addint(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final boolean success =
result("addint3D",
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
javaResult, rsResult);
inputAllocation.destroy();
return success;
}
//-----------------------------------------------------------------
private boolean patternInterleavedReduce(RenderScript RS, ScriptC_reduce s) {
// Run two reduce operations without forcing completion between them.
// We want to ensure that the driver can handle this, and that
// temporary Allocations created to run the reduce operations survive
// until get().
boolean pass = true;
final int inputSize = (1 << 18);
final int[] input1 = createInputArrayInt(123, Integer.MAX_VALUE / inputSize);
final int[] input2 = createInputArrayInt(456, Integer.MAX_VALUE / inputSize);
final int javaResult1 = addint(input1);
final int javaResult2 = addint(input2);
final ScriptC_reduce.result_int rsResultFuture1 = s.reduce_addint(input1);
final ScriptC_reduce.result_int rsResultFuture2 = s.reduce_addint(input2);
pass &= result("patternInterleavedReduce (1)", new timing(inputSize),
javaResult1, rsResultFuture1.get());
pass &= result("patternInterleavedReduce (2)", new timing(inputSize),
javaResult2, rsResultFuture2.get());
return pass;
}
//-----------------------------------------------------------------
private int[] sillySumIntoDecArray(final int[] input) {
final int resultScalar = addint(input);
final int[] result = new int[4];
for (int i = 0; i < 4; ++i)
result[i] = resultScalar / (i + 1);
return result;
}
private int[] sillySumIntoIncArray(final int[] input) {
final int resultScalar = addint(input);
final int[] result = new int[4];
for (int i = 0; i < 4; ++i)
result[i] = resultScalar / (4 - i);
return result;
}
private boolean patternDuplicateAnonymousResult(RenderScript RS, ScriptC_reduce s) {
// Ensure that we can have two kernels with the same anonymous result type.
boolean pass = true;
final int inputSize = 1000;
final int[] input = createInputArrayInt(149, Integer.MAX_VALUE / inputSize);
final int[] javaResultDec = sillySumIntoDecArray(input);
final int[] rsResultDec = s.reduce_sillySumIntoDecArray(input).get();
pass &= result("patternDuplicateAnonymousResult (Dec)", new timing(inputSize),
javaResultDec, rsResultDec);
final int[] javaResultInc = sillySumIntoIncArray(input);
final int[] rsResultInc = s.reduce_sillySumIntoIncArray(input).get();
pass &= result("patternDuplicateAnonymousResult (Inc)", new timing(inputSize),
javaResultInc, rsResultInc);
return pass;
}
///////////////////////////////////////////////////////////////////
private float findMinAbs(float[] input) {
float accum = input[0];
for (int idx = 1; idx < input.length; ++idx) {
final float val = input[idx];
if (Math.abs(val) < Math.abs(accum))
accum = val;
}
return accum;
}
static interface ReduceFindMinAbs {
float run(Allocation input);
}
private boolean findMinAbs(RenderScript RS, float[] inputArray, String testName, ReduceFindMinAbs reduction) {
final long javaTimeStart = java.lang.System.currentTimeMillis();
final float javaResult = findMinAbs(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocation = Allocation.createSized(RS, Element.F32(RS), inputArray.length);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copyFrom(inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final float rsResult = reduction.run(inputAllocation);
final long rsTimeEnd = java.lang.System.currentTimeMillis();
// Note that the Java and RenderScript algorithms are not
// guaranteed to find the same results -- but the results
// should have the same absolute value.
final boolean success =
result(testName,
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
Math.abs(javaResult), Math.abs(rsResult));
inputAllocation.destroy();
return success;
}
private boolean findMinAbsBool(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
return findMinAbs(RS, createInputArrayFloat(size[0], seed), "findMinAbsBool",
(Allocation input) -> s.reduce_findMinAbsBool(input).get());
}
private boolean findMinAbsBoolInf(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
return findMinAbs(RS, createInputArrayFloatWithInfs(size[0], 1 + size[0] / 1000, seed), "findMinAbsBoolInf",
(Allocation input) -> s.reduce_findMinAbsBool(input).get());
}
private boolean findMinAbsNaN(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
return findMinAbs(RS, createInputArrayFloat(size[0], seed), "findMinAbsNaN",
(Allocation input) -> s.reduce_findMinAbsNaN(input).get());
}
private boolean findMinAbsNaNInf(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
return findMinAbs(RS, createInputArrayFloatWithInfs(size[0], 1 + size[0] / 1000, seed), "findMinAbsNaNInf",
(Allocation input) -> s.reduce_findMinAbsNaN(input).get());
}
///////////////////////////////////////////////////////////////////
private Int2 findMinAndMax(float[] input) {
float minVal = Float.POSITIVE_INFINITY;
int minIdx = -1;
float maxVal = Float.NEGATIVE_INFINITY;
int maxIdx = -1;
for (int idx = 0; idx < input.length; ++idx) {
if ((minIdx < 0) || (input[idx] < minVal)) {
minVal = input[idx];
minIdx = idx;
}
if ((maxIdx < 0) || (input[idx] > maxVal)) {
maxVal = input[idx];
maxIdx = idx;
}
}
return new Int2(minIdx, maxIdx);
}
private boolean findMinAndMax_array(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final float[] input = createInputArrayFloat(size[0], seed);
final Int2 javaResult = findMinAndMax(input);
final Int2 rsResult = s.reduce_findMinAndMax(input).get();
// Note that the Java and RenderScript algorithms are not
// guaranteed to find the same cells -- but they should
// find cells of the same value.
final Float2 javaVal = new Float2(input[javaResult.x], input[javaResult.y]);
final Float2 rsVal = new Float2(input[rsResult.x], input[rsResult.y]);
return result("findMinAndMax_array", new timing(size[0]), javaVal, rsVal);
}
private boolean findMinAndMax(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final float[] inputArray = createInputArrayFloat(size[0], seed);
final long javaTimeStart = java.lang.System.currentTimeMillis();
final Int2 javaResult = findMinAndMax(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocation = Allocation.createSized(RS, Element.F32(RS), inputArray.length);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copyFrom(inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final Int2 rsResult = s.reduce_findMinAndMax(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
// Note that the Java and RenderScript algorithms are not
// guaranteed to find the same cells -- but they should
// find cells of the same value.
final Float2 javaVal = new Float2(inputArray[javaResult.x], inputArray[javaResult.y]);
final Float2 rsVal = new Float2(inputArray[rsResult.x], inputArray[rsResult.y]);
final boolean success =
result("findMinAndMax",
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
javaVal, rsVal);
inputAllocation.destroy();
return success;
}
//-----------------------------------------------------------------
private boolean patternFindMinAndMaxInf(RenderScript RS, ScriptC_reduce s) {
// Run this kernel on an input consisting solely of a single infinity.
final float[] input = new float[1];
input[0] = Float.POSITIVE_INFINITY;
final Int2 javaResult = findMinAndMax(input);
final Int2 rsResult = s.reduce_findMinAndMax(input).get();
// Note that the Java and RenderScript algorithms are not
// guaranteed to find the same cells -- but they should
// find cells of the same value.
final Float2 javaVal = new Float2(input[javaResult.x], input[javaResult.y]);
final Float2 rsVal = new Float2(input[rsResult.x], input[rsResult.y]);
return result("patternFindMinAndMaxInf", new timing(1), javaVal, rsVal);
}
///////////////////////////////////////////////////////////////////
// Both the input and the result are linearized representations of matSize*matSize matrices.
private float[] findMinMat(final float[] inputArray, final int matSize) {
final int matSizeSquared = matSize * matSize;
float[] result = new float[matSizeSquared];
for (int i = 0; i < matSizeSquared; ++i)
result[i] = Float.POSITIVE_INFINITY;
for (int i = 0; i < inputArray.length; ++i)
result[i % matSizeSquared] = Math.min(result[i % matSizeSquared], inputArray[i]);
return result;
}
static interface ReduceFindMinMat {
float[] run(Allocation input);
}
private boolean findMinMat(RenderScript RS, int seed, int[] inputSize,
int matSize, Element matElement, ReduceFindMinMat reduction) {
final int length = inputSize[0];
final int matSizeSquared = matSize * matSize;
final float[] inputArray = createInputArrayFloat(matSizeSquared * length, seed);
final long javaTimeStart = java.lang.System.currentTimeMillis();
final float[] javaResult = findMinMat(inputArray, matSize);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocation = Allocation.createSized(RS, matElement, length);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copyFromUnchecked(inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final float[] rsResult = reduction.run(inputAllocation);
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final boolean success =
result("findMinMat" + matSize,
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
javaResult, rsResult);
inputAllocation.destroy();
return success;
}
private boolean findMinMat2(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
return findMinMat(RS, seed, size, 2, Element.MATRIX_2X2(RS),
(Allocation input) -> s.reduce_findMinMat2(input).get());
}
private boolean findMinMat4(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
return findMinMat(RS, seed, size, 4, Element.MATRIX_4X4(RS),
(Allocation input) -> s.reduce_findMinMat4(input).get());
}
///////////////////////////////////////////////////////////////////
private int fz(final int[] input) {
for (int i = 0; i < input.length; ++i)
if (input[i] == 0)
return i;
return -1;
}
private boolean fz_array(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final int inputLen = size[0];
int[] input = createInputArrayInt(inputLen, seed + 0);
// just in case we got unlucky
input[(new Random(seed + 1)).nextInt(inputLen)] = 0;
final int rsResult = s.reduce_fz(input).get();
final boolean success = (input[rsResult] == 0);
Log.i(TAG,
"fz_array: input[" + rsResult + "] == " + input[rsResult] + ": " +
(success ? "PASSED " + timing.string(size[0]) : "FAILED"));
return success;
}
private boolean fz(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final int inputLen = size[0];
int[] inputArray = createInputArrayInt(inputLen, seed + 0);
// just in case we got unlucky
inputArray[(new Random(seed + 1)).nextInt(inputLen)] = 0;
final long javaTimeStart = java.lang.System.currentTimeMillis();
final int javaResult = fz(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocation = Allocation.createSized(RS, Element.I32(RS), inputArray.length);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copyFrom(inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final int rsResult = s.reduce_fz(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final boolean success = (inputArray[rsResult] == 0);
String status = (success ? "PASSED" : "FAILED");
if (success)
status += " " + timing.string(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation);
Log.i(TAG,
"fz: java input[" + javaResult + "] == " + inputArray[javaResult] +
", rs input[" + rsResult + "] == " + inputArray[javaResult] + ": " + status);
inputAllocation.destroy();
return success;
}
///////////////////////////////////////////////////////////////////
private boolean fz2(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final int dimX = size[0], dimY = size[1];
final int inputLen = dimX * dimY;
int[] inputArray = createInputArrayInt(inputLen, seed + 0);
// just in case we got unlucky
inputArray[(new Random(seed + 1)).nextInt(inputLen)] = 0;
final long javaTimeStart = java.lang.System.currentTimeMillis();
final int javaResultLinear = fz(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final Int2 javaResult = new Int2(javaResultLinear % dimX, javaResultLinear / dimX);
final int javaCellVal = inputArray[javaResult.x + dimX * javaResult.y];
final long rsTimeStart = java.lang.System.currentTimeMillis();
Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
typeBuilder.setX(dimX).setY(dimY);
Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copy2DRangeFrom(0, 0, dimX, dimY, inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final Int2 rsResult = s.reduce_fz2(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final int rsCellVal = inputArray[rsResult.x + dimX * rsResult.y];
final boolean success = (rsCellVal == 0);
String status = (success ? "PASSED" : "FAILED");
if (success)
status += " " + timing.string(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation);
Log.i(TAG,
"fz2: java input[" + javaResult.x + ", " + javaResult.y + "] == " + javaCellVal +
", rs input[" + rsResult.x + ", " + rsResult.y + "] == " + rsCellVal + ": " + status);
inputAllocation.destroy();
return success;
}
///////////////////////////////////////////////////////////////////
private boolean fz3(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
final int dimX = size[0], dimY = size[1], dimZ = size[2];
final int inputLen = dimX * dimY * dimZ;
int[] inputArray = createInputArrayInt(inputLen, seed + 0);
// just in case we got unlucky
inputArray[(new Random(seed + 1)).nextInt(inputLen)] = 0;
final long javaTimeStart = java.lang.System.currentTimeMillis();
final int javaResultLinear = fz(inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final Int3 javaResult = new Int3(
javaResultLinear % dimX,
(javaResultLinear / dimX) % dimY,
javaResultLinear / (dimX * dimY));
final int javaCellVal = inputArray[javaResult.x + dimX * javaResult.y + dimX * dimY * javaResult.z];
final long rsTimeStart = java.lang.System.currentTimeMillis();
Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
typeBuilder.setX(dimX).setY(dimY).setZ(dimZ);
Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copy3DRangeFrom(0, 0, 0, dimX, dimY, dimZ, inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final Int3 rsResult = s.reduce_fz3(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final int rsCellVal = inputArray[rsResult.x + dimX * rsResult.y + dimX * dimY * rsResult.z];
final boolean success = (rsCellVal == 0);
String status = (success ? "PASSED" : "FAILED");
if (success)
status += " " + timing.string(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation);
Log.i(TAG,
"fz3: java input[" + javaResult.x + ", " + javaResult.y + ", " + javaResult.z + "] == " + javaCellVal +
", rs input[" + rsResult.x + ", " + rsResult.y + ", " + rsResult.z + "] == " + rsCellVal + ": " + status);
inputAllocation.destroy();
return success;
}
///////////////////////////////////////////////////////////////////
private static final int histogramBucketCount = 256;
private long[] histogram(RenderScript RS, final byte[] inputArray) {
Allocation inputAllocation = Allocation.createSized(RS, Element.U8(RS), inputArray.length);
inputAllocation.copyFrom(inputArray);
Allocation outputAllocation = Allocation.createSized(RS, Element.U32(RS), histogramBucketCount);
ScriptIntrinsicHistogram scriptHsg = ScriptIntrinsicHistogram.create(RS, Element.U8(RS));
scriptHsg.setOutput(outputAllocation);
scriptHsg.forEach(inputAllocation);
int[] outputArrayMistyped = new int[histogramBucketCount];
outputAllocation.copyTo(outputArrayMistyped);
long[] outputArray = new long[histogramBucketCount];
for (int i = 0; i < histogramBucketCount; ++i)
outputArray[i] = outputArrayMistyped[i] & (long) 0xffffffff;
inputAllocation.destroy();
outputAllocation.destroy();
scriptHsg.destroy();
return outputArray;
}
private boolean histogram_array(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final byte[] inputArray = createInputArrayByte(size[0], seed);
final long[] javaResult = histogram(RS, inputArray);
assertEquals("javaResult length", histogramBucketCount, javaResult.length);
final long[] rsResult = s.reduce_histogram(inputArray).get();
assertEquals("rsResult length", histogramBucketCount, rsResult.length);
return result("histogram_array", new timing(size[0]), javaResult, rsResult);
}
private boolean histogram(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final byte[] inputArray = createInputArrayByte(size[0], seed);
final long javaTimeStart = java.lang.System.currentTimeMillis();
final long[] javaResult = histogram(RS, inputArray);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
assertEquals("javaResult length", histogramBucketCount, javaResult.length);
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocation = Allocation.createSized(RS, Element.U8(RS), inputArray.length);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocation.copyFrom(inputArray);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final long[] rsResult = s.reduce_histogram(inputAllocation).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
assertEquals("rsResult length", histogramBucketCount, rsResult.length);
// NOTE: The "java time" is actually for the RenderScript histogram intrinsic
final boolean success =
result("histogram",
new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
javaResult, rsResult);
inputAllocation.destroy();
return success;
}
//-----------------------------------------------------------------
private boolean patternRedundantGet(RenderScript RS, ScriptC_reduce s) {
// Ensure that get() can be called multiple times on the same
// result, and returns the same object each time.
boolean pass = true;
final int inputLength = 1 << 18;
final byte[] inputArray = createInputArrayByte(inputLength, 789);
final long[] javaResult = histogram(RS, inputArray);
assertEquals("javaResult length", histogramBucketCount, javaResult.length);
final ScriptC_reduce.resultArray256_uint rsResultFuture = s.reduce_histogram(inputArray);
final long[] rsResult1 = rsResultFuture.get();
assertEquals("rsResult1 length", histogramBucketCount, rsResult1.length);
pass &= result("patternRedundantGet (1)", new timing(inputLength), javaResult, rsResult1);
final long[] rsResult2 = rsResultFuture.get();
pass &= result("patternRedundantGet (2)", new timing(inputLength), javaResult, rsResult2);
final boolean success = (rsResult1 == rsResult2);
Log.i(TAG, "patternRedundantGet (object equality): " + (success ? "PASSED" : "FAILED"));
pass &= success;
return pass;
}
//-----------------------------------------------------------------
private Int2 mode(RenderScript RS, final byte[] inputArray) {
long[] hsg = histogram(RS, inputArray);
int modeIdx = 0;
for (int i = 1; i < hsg.length; ++i)
if (hsg[i] > hsg[modeIdx]) modeIdx = i;
return new Int2(modeIdx, (int) hsg[modeIdx]);
}
private boolean mode_array(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final byte[] inputArray = createInputArrayByte(size[0], seed);
final Int2 javaResult = mode(RS, inputArray);
final Int2 rsResult = s.reduce_mode(inputArray).get();
return result("mode", new timing(size[0]), javaResult, rsResult);
}
///////////////////////////////////////////////////////////////////
private long sumgcd(final int in1[], final int in2[]) {
assertEquals("sumgcd input lengths", in1.length, in2.length);
long sum = 0;
for (int i = 0; i < in1.length; ++i) {
int a = in1[i], b = in2[i];
while (b != 0) {
final int aNew = b;
final int bNew = a % b;
a = aNew;
b = bNew;
}
sum += a;
}
return sum;
}
private boolean sumgcd(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
final int len = size[0];
final int[] inputArrayA = createInputArrayInt(len, seed + 0);
final int[] inputArrayB = createInputArrayInt(len, seed + 1);
final long javaTimeStart = java.lang.System.currentTimeMillis();
final long javaResult = sumgcd(inputArrayA, inputArrayB);
final long javaTimeEnd = java.lang.System.currentTimeMillis();
final long rsTimeStart = java.lang.System.currentTimeMillis();
Allocation inputAllocationA = Allocation.createSized(RS, Element.I32(RS), len);
Allocation inputAllocationB = Allocation.createSized(RS, Element.I32(RS), len);
final long copyTimeStart = java.lang.System.currentTimeMillis();
inputAllocationA.copyFrom(inputArrayA);
inputAllocationB.copyFrom(inputArrayB);
final long kernelTimeStart = java.lang.System.currentTimeMillis();
final long rsResult = s.reduce_sumgcd(inputAllocationA, inputAllocationB).get();
final long rsTimeEnd = java.lang.System.currentTimeMillis();
final boolean success =
result("sumgcd",
new timing(javaTimeStart, javaTimeEnd, rsTimeStart, copyTimeStart, kernelTimeStart, rsTimeEnd,
inputAllocationA, inputAllocationB),
javaResult, rsResult);
inputAllocationA.destroy();
inputAllocationB.destroy();
return success;
}
///////////////////////////////////////////////////////////////////
// Return an array of sparse integer values from 0 to maxVal inclusive.
// The array consists of all values k*sparseness (k a nonnegative integer)
// that are less than maxVal, and maxVal itself. For example, if maxVal
// is 20 and sparseness is 6, then the result is { 0, 6, 12, 18, 20 };
// and if maxVal is 20 and sparseness is 10, then the result is { 0, 10, 20 }.
//
// The elements of the array are sorted in increasing order.
//
// maxVal -- must be nonnegative
// sparseness -- must be positive
private static int[] computeSizePoints(int maxVal, int sparseness) {
assertTrue((maxVal >= 0) && (sparseness > 0));
final boolean maxValIsExtra = ((maxVal % sparseness) != 0);
int[] result = new int[1 + maxVal / sparseness + (maxValIsExtra ? 1 : 0)];
for (int i = 0; i * sparseness <= maxVal; ++i)
result[i] = i * sparseness;
if (maxValIsExtra)
result[result.length - 1] = maxVal;
return result;
}
private static final int maxSeedsPerTest = 10;
static interface Test {
// A test execution is characterized by two properties: A seed
// and a size.
//
// The seed is used for generating pseudorandom input data.
// Ideally, we use different seeds for different tests and for
// different executions of the same test at different sizes.
// A test with multiple blocks of input data (i.e., for a
// reduction with multiple inputs) may want multiple seeds; it
// may use the seeds seed..seed+maxSeedsPerTest-1.
//
// The size indicates the amount of input data. It is the number
// of cells in a particular dimension of the iteration space.
boolean run(RenderScript RS, ScriptC_reduce s, int seed, int[] size);
}
static class TestDescription {
public TestDescription(String myTestName, Test myTest, int mySeed, int[] myDefSize,
int myLog2MaxSize, int mySparseness) {
testName = myTestName;
test = myTest;
seed = mySeed;
defSize = myDefSize;
log2MaxSize = myLog2MaxSize;
sparseness = mySparseness;
}
public TestDescription(String myTestName, Test myTest, int mySeed, int[] myDefSize, int myLog2MaxSize) {
testName = myTestName;
test = myTest;
seed = mySeed;
defSize = myDefSize;
log2MaxSize = myLog2MaxSize;
sparseness = 1;
}
public TestDescription(String myTestName, Test myTest, int mySeed, int[] myDefSize) {
testName = myTestName;
test = myTest;
seed = mySeed;
defSize = myDefSize;
log2MaxSize = -1;
sparseness = 1;
}
public final String testName;
public final Test test;
// When executing the test, scale this up by maxSeedsPerTest.
public final int seed;
// If we're only going to run the test once, what size should
// we use? The length of the array is the number of
// dimensions of the input data.
public final int[] defSize;
// If we're going to run the test over a range of sizes, what
// is the maximum size to use? (This constrains the number of
// cells of the input data, not the number of cells ALONG A
// PARTICULAR DIMENSION of the input data.)
public final int log2MaxSize;
// If we're going to run the test "exhaustively" over a range
// of sizes, what is the size of a step through the range?
//
// For 1D, must be 1.
public final int sparseness;
}
private boolean run(TestDescription td, RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
String arrayContent = "";
for (int i = 0; i < size.length; ++i) {
if (i != 0)
arrayContent += ", ";
arrayContent += size[i];
}
Log.i(TAG, "Running " + td.testName + "(seed = " + seed + ", size[] = {" + arrayContent + "})");
return td.test.run(RS, s, seed, size);
}
private final TestDescription[] correctnessTests = {
// alloc and array variants of the same test will use the same
// seed, in case results need to be compared.
new TestDescription("addint1D", this::addint1D, 0, new int[]{100000}, 20),
new TestDescription("addint1D_array", this::addint1D_array, 0, new int[]{100000}, 20),
new TestDescription("addint2D", this::addint2D, 1, new int[]{450, 225}, 20, 5),
new TestDescription("addint3D", this::addint3D, 2, new int[]{37, 48, 49}, 20, 7),
// Bool and NaN variants of the same test will use the same
// seed, in case results need to be compared.
new TestDescription("findMinAbsBool", this::findMinAbsBool, 3, new int[]{100000}, 20),
new TestDescription("findMinAbsNaN", this::findMinAbsNaN, 3, new int[]{100000}, 20),
new TestDescription("findMinAbsBoolInf", this::findMinAbsBoolInf, 4, new int[]{100000}, 20),
new TestDescription("findMinAbsNaNInf", this::findMinAbsNaNInf, 4, new int[]{100000}, 20),
new TestDescription("findMinAndMax", this::findMinAndMax, 5, new int[]{100000}, 20),
new TestDescription("findMinAndMax_array", this::findMinAndMax_array, 5, new int[]{100000}, 20),
new TestDescription("findMinMat2", this::findMinMat2, 6, new int[]{25000}, 17),
new TestDescription("findMinMat4", this::findMinMat4, 7, new int[]{10000}, 15),
new TestDescription("fz", this::fz, 8, new int[]{100000}, 20),
new TestDescription("fz_array", this::fz_array, 8, new int[]{100000}, 20),
new TestDescription("fz2", this::fz2, 9, new int[]{225, 450}, 20, 5),
new TestDescription("fz3", this::fz3, 10, new int[]{59, 48, 37}, 20, 7),
new TestDescription("histogram", this::histogram, 11, new int[]{100000}, 20),
new TestDescription("histogram_array", this::histogram_array, 11, new int[]{100000}, 20),
// might want to add: new TestDescription("mode", this::mode, 12, new int[]{100000}, 20),
new TestDescription("mode_array", this::mode_array, 12, new int[]{100000}, 20),
new TestDescription("sumgcd", this::sumgcd, 13, new int[]{1 << 16}, 20)
};
private boolean runCorrectnessQuick(RenderScript RS, ScriptC_reduce s) {
boolean pass = true;
for (TestDescription td : correctnessTests) {
pass &= run(td, RS, s, maxSeedsPerTest * td.seed, td.defSize);
}
return pass;
}
// NOTE: Each test execution gets maxSeedsPerTest, and there are
// up to 3 + 5*log2MaxSize test executions in the full (as opposed
// to quick) correctness run of a particular test description, and
// we need an additional seed for pseudorandom size generation.
// Assuming log2MaxSize does not exceed 32, then it should be
// sufficient to reserve 1 + (3+5*32)*maxSeedsPerTest seeds per
// TestDescription.
//
// See runCorrectness1D().
private static final int seedsPerTestDescriptionCorrectness1D = 1 + (3 + 5 * 32) * maxSeedsPerTest;
// NOTE: Each test execution gets maxSeedsPerTest, and there are
// about 11*((log2MaxSize+1)**2) test executions in the full (as
// opposed to quick) correctness run of a particular test
// description, and we need a seed for pseudorandom size
// generation. Assuming log2MaxSize does not exceed 32, then it
// should be sufficient to reserve 1 + 11*1089*maxSeedsPerTest
// seeds per TestDescription.
//
// See runCorrectness2D().
private static final int seedsPerTestDescriptionCorrectness2D = 1 + (11 * 1089) * maxSeedsPerTest;
// NOTE: Each test execution gets maxSeedsPerTest, and there are
// about 27*((log2MaxSize+1)**3) + 6*((log2MaxSize+1)**2) test
// executions in the full (as opposed to quick) correctness run of
// a particular test description, and we need a seed for (c).
// Assuming log2MaxSize does not exceed 32, then it should
// be sufficient to reserve 1 + (27*(33**3) + 6*(33**2))*maxSeedsPerTest
// seeds per TestDescription, which can be simplified upwards to
// 1 + (28*(33**3))*maxSeedsPerTest seeds per TestDescription.
private static final int seedsPerTestDescriptionCorrectness3D = 1 + (28 * 35937) * maxSeedsPerTest;
// Each test execution gets a certain number of seeds, and a full
// (as opposed to quick) correctness run of a particular
// TestDescription consists of some number of executions (each of
// which needs up to maxSeedsPerTest) and may require some
// additional seeds.
private static final int seedsPerTestDescriptionCorrectness =
Math.max(seedsPerTestDescriptionCorrectness1D,
Math.max(seedsPerTestDescriptionCorrectness2D,
seedsPerTestDescriptionCorrectness3D));
private boolean runCorrectness(RenderScript RS, ScriptC_reduce s) {
boolean pass = true;
for (TestDescription td : correctnessTests) {
switch (td.defSize.length) {
case 1:
pass &= runCorrectness1D(td, RS, s);
break;
case 2:
pass &= runCorrectness2D(td, RS, s);
break;
case 3:
pass &= runCorrectness3D(td, RS, s);
break;
default:
assertTrue("unexpected defSize.length " + td.defSize.length, false);
pass &= false;
break;
}
}
return pass;
}
private boolean runCorrectness1D(TestDescription td, RenderScript RS, ScriptC_reduce s) {
assertEquals(1, td.sparseness);
final int log2MaxSize = td.log2MaxSize;
assertTrue(log2MaxSize >= 0);
boolean pass = true;
// We will execute the test with the following sizes:
// (a) Each power of 2 from zero (2**0) up to log2MaxSize (2**log2MaxSize)
// (b) Each size from (a) +/-1
// (c) 2 random sizes between each pair of adjacent points in (a)
int[] testSizes = new int[
/* a */ (1 + log2MaxSize) +
/* b */ 2 * (1 + log2MaxSize) +
/* c */ 2 * log2MaxSize];
// See seedsPerTestDescriptionCorrectness1D
final int seedForPickingTestSizes = td.seed * seedsPerTestDescriptionCorrectness;
int nextTestIdx = 0;
// Fill in (a) and (b)
for (int i = 0; i <= log2MaxSize; ++i) {
final int pwrOf2 = 1 << i;
testSizes[nextTestIdx++] = pwrOf2; /* a */
testSizes[nextTestIdx++] = pwrOf2 - 1; /* b */
testSizes[nextTestIdx++] = pwrOf2 + 1; /* b */
}
// Fill in (c)
Random r = new Random(seedForPickingTestSizes);
for (int i = 0; i < log2MaxSize; ++i) {
final int lo = (1 << i) + 1;
final int hi = 1 << (i + 1);
if (lo < hi) {
for (int j = 0; j < 2; ++j) {
testSizes[nextTestIdx++] = r.nextInt(hi - lo) + lo;
}
}
}
Arrays.sort(testSizes);
int[] lastTestSizeArg = new int[]{-1};
for (int i = 0; i < testSizes.length; ++i) {
if ((testSizes[i] > 0) && (testSizes[i] != lastTestSizeArg[0])) {
lastTestSizeArg[0] = testSizes[i];
final int seedForTestExecution = seedForPickingTestSizes + 1 + i * maxSeedsPerTest;
pass &= run(td, RS, s, seedForTestExecution, lastTestSizeArg);
}
}
return pass;
}
private boolean runCorrectness2D(TestDescription td, RenderScript RS, ScriptC_reduce s) {
final int log2MaxSize = td.log2MaxSize, maxSize = 1 << log2MaxSize, sparseness = td.sparseness;
assertTrue((log2MaxSize >= 0) && (sparseness >= 1));
boolean pass = true;
final int[] sizePoints = computeSizePoints(log2MaxSize, sparseness);
// We will execute the test with the following sizes:
// (a) Each dimension at a power of 2 from sizePoints[]
/// such that the sum of the exponents does not exceed
// log2MaxSize
// (b) Each size from (a) with one or both dimensions +/-1,
// except where this would exceed 2**log2MaxSize
// (c) Approximately 2*(sizePoints.length**2) random sizes
ArrayList<int[]> testSizesList = new ArrayList<int[]>();
// See seedsPerTestDescriptionCorrectness2D
final int seedForPickingTestSizes = td.seed * seedsPerTestDescriptionCorrectness;
// Fill in (a) and (b)
for (int i : sizePoints) {
final int iPwrOf2 = 1 << i;
for (int iDelta = -1; iDelta <= 1; ++iDelta) {
final int iSize = iPwrOf2 + iDelta;
for (int j : sizePoints) {
final int jPwrOf2 = 1 << j;
for (int jDelta = -1; jDelta <= 1; ++jDelta) {
final int jSize = jPwrOf2 + jDelta;
if ((long) iSize * (long) jSize <= maxSize)
testSizesList.add(new int[]{iSize, jSize});
}
}
}
}
// Fill in (c)
Random r = new Random(seedForPickingTestSizes);
for (int i : sizePoints) {
for (int j : sizePoints) {
final int size0 = 1 + r.nextInt(1 << i);
final int size1 = 1 + r.nextInt(maxSize / size0);
testSizesList.add(new int[]{size0, size1});
testSizesList.add(new int[]{size1, size0});
}
}
int[][] testSizes = testSizesList.toArray(new int[0][]);
Arrays.sort(testSizes,
(a, b) -> {
final int comp0 = ((Integer) a[0]).compareTo(b[0]);
return (comp0 != 0 ? comp0 : ((Integer) a[1]).compareTo(b[1]));
});
int[] lastTestSizeArg = null;
for (int i = 0; i < testSizes.length; ++i) {
if ((testSizes[i][0] <= 0) || (testSizes[i][1] <= 0))
continue;
if ((lastTestSizeArg != null) &&
(testSizes[i][0] == lastTestSizeArg[0]) &&
(testSizes[i][1] == lastTestSizeArg[1]))
continue;
lastTestSizeArg = testSizes[i];
final int seedForTestExecution = seedForPickingTestSizes + 1 + i * maxSeedsPerTest;
pass &= run(td, RS, s, seedForTestExecution, lastTestSizeArg);
}
return pass;
}
private boolean runCorrectness3D(TestDescription td, RenderScript RS, ScriptC_reduce s) {
final int log2MaxSize = td.log2MaxSize, maxSize = 1 << log2MaxSize, sparseness = td.sparseness;
assertTrue((log2MaxSize >= 0) && (sparseness >= 1));
boolean pass = true;
final int[] sizePoints = computeSizePoints(log2MaxSize, sparseness);
// We will execute the test with the following sizes:
// (a) Each dimension at a power of 2 from sizePoints[]
/// such that the sum of the exponents does not exceed
// log2MaxSize
// (b) Each size from (a) with one or both dimensions +/-1,
// except where this would exceed 2**log2MaxSize
// (c) Approximately 6*(sizePoints.length**2) random sizes
ArrayList<int[]> testSizesList = new ArrayList<int[]>();
// See seedsPerTestDescriptionCorrectness3D
final int seedForPickingTestSizes = td.seed * seedsPerTestDescriptionCorrectness;
// Fill in (a) and (b)
for (int i : sizePoints) {
final int iPwrOf2 = 1 << i;
for (int iDelta = -1; iDelta <= 1; ++iDelta) {
final int iSize = iPwrOf2 + iDelta;
for (int j : sizePoints) {
final int jPwrOf2 = 1 << j;
for (int jDelta = -1; jDelta <= 1; ++jDelta) {
final int jSize = jPwrOf2 + jDelta;
for (int k : sizePoints) {
final int kPwrOf2 = 1 << k;
for (int kDelta = -1; kDelta <= 1; ++kDelta) {
final int kSize = kPwrOf2 + kDelta;
if ((long) iSize * (long) jSize * (long) kSize <= maxSize)
testSizesList.add(new int[]{iSize, jSize, kSize});
}
}
}
}
}
}
// Fill in (c)
Random r = new Random(seedForPickingTestSizes);
for (int i : sizePoints) {
for (int j : sizePoints) {
final int size0 = 1 + r.nextInt(1 << i);
final int size1 = 1 + r.nextInt(Math.min(1 << j, maxSize / size0));
final int size2 = 1 + r.nextInt(maxSize / (size0 * size1));
testSizesList.add(new int[]{size0, size1, size2});
testSizesList.add(new int[]{size0, size2, size1});
testSizesList.add(new int[]{size1, size0, size2});
testSizesList.add(new int[]{size1, size2, size0});
testSizesList.add(new int[]{size2, size0, size1});
testSizesList.add(new int[]{size2, size1, size0});
}
}
int[][] testSizes = testSizesList.toArray(new int[0][]);
Arrays.sort(testSizes,
(a, b) -> {
int comp = ((Integer) a[0]).compareTo(b[0]);
if (comp == 0)
comp = ((Integer) a[1]).compareTo(b[1]);
if (comp == 0)
comp = ((Integer) a[2]).compareTo(b[2]);
return comp;
});
int[] lastTestSizeArg = null;
for (int i = 0; i < testSizes.length; ++i) {
if ((testSizes[i][0] <= 0) || (testSizes[i][1] <= 0) || (testSizes[i][2] <= 0))
continue;
if ((lastTestSizeArg != null) &&
(testSizes[i][0] == lastTestSizeArg[0]) &&
(testSizes[i][1] == lastTestSizeArg[1]) &&
(testSizes[i][2] == lastTestSizeArg[2]))
continue;
// Apply Z-dimension limiting.
//
// The Z dimension is always handled specially by GPU
// drivers, and a high value for this dimension can have
// serious performance implications. For example, Cuda
// and OpenCL encourage Z to be the smallest dimension.
if (testSizes[i][2] > 1024)
continue;
lastTestSizeArg = testSizes[i];
final int seedForTestExecution = seedForPickingTestSizes + 1 + i * maxSeedsPerTest;
pass &= run(td, RS, s, seedForTestExecution, lastTestSizeArg);
}
return pass;
}
private final TestDescription[] performanceTests = {
new TestDescription("addint1D", this::addint1D, 0, new int[]{100000 << 10}),
new TestDescription("addint2D", this::addint2D, 1, new int[]{450 << 5, 225 << 5}),
new TestDescription("addint3D", this::addint3D, 2, new int[]{37 << 3, 48 << 3, 49 << 3}),
new TestDescription("findMinAndMax", this::findMinAndMax, 3, new int[]{100000 << 9}),
new TestDescription("fz", this::fz, 4, new int[]{100000 << 10}),
new TestDescription("fz2", this::fz2, 5, new int[]{225 << 5, 450 << 5}),
new TestDescription("fz3", this::fz3, 6, new int[]{59 << 3, 48 << 3, 37 << 3}),
new TestDescription("histogram", this::histogram, 7, new int[]{100000 << 10}),
// might want to add: new TestDescription("mode", this::mode, 8, new int[]{100000}),
new TestDescription("sumgcd", this::sumgcd, 9, new int[]{1 << 21})
};
private boolean runPerformanceQuick(RenderScript RS, ScriptC_reduce s) {
boolean pass = true;
for (TestDescription td : performanceTests) {
pass &= run(td, RS, s, maxSeedsPerTest * td.seed, td.defSize);
}
return pass;
}
private boolean runCorrectnessPatterns(RenderScript RS, ScriptC_reduce s) {
// Test some very specific usage patterns.
boolean pass = true;
pass &= patternDuplicateAnonymousResult(RS, s);
pass &= patternFindMinAndMaxInf(RS, s);
pass &= patternInterleavedReduce(RS, s);
pass &= patternRedundantGet(RS, s);
return pass;
}
public void run() {
RenderScript pRS = RenderScript.create(mCtx);
ScriptC_reduce s = new ScriptC_reduce(pRS);
s.set_negInf(Float.NEGATIVE_INFINITY);
s.set_posInf(Float.POSITIVE_INFINITY);
boolean pass = true;
pass &= runCorrectnessPatterns(pRS, s);
pass &= runCorrectnessQuick(pRS, s);
pass &= runCorrectness(pRS, s);
// pass &= runPerformanceQuick(pRS, s);
pRS.finish();
s.destroy();
pRS.destroy();
Log.i(TAG, pass ? "PASSED" : "FAILED");
if (pass)
passTest();
else
failTest();
}
}
// TODO: Add machinery for easily running fuller (i.e., non-sparse) testing.