I am trying to profile two implementation ideas of multidimensional array classes.
In the code below, application creates thee huge multidimensional array of three classes: Array1
, Array2
and conventional.
Application first creates arrays, which is not measured. Then it fills each array and read each of them by order. Time measurement occurs both programmatically and by profiler.
Unfortunately, section timings are not coincide. For example, the methods or class Array2 which consumes nearly half time, are not listed in profiler at all.
Why?
main code:
package tests;
public class Runner01 {
private static final int[] dims = {400, 100, 20, 300};
static private double[] data;
static private Array1 array1;
static private Array2 array2;
static private double[][][][] array3;
public static interface MyRunnable {
public void run(int ii, int i, int j, int k, int l);
}
public static long test(MyRunnable runnable) {
long start = System.nanoTime();
int ii = 0;
for (int i = 0; i < dims[0]; ++i) {
for (int j = 0; j < dims[1]; ++j) {
for (int k = 0; k < dims[2]; ++k) {
for (int l = 0; l < dims[3]; ++l) {
runnable.run(ii, i, j, k, l);
ii++;
}
}
}
}
long end = System.nanoTime();
System.out.println("Done in " + (double) (end - start) / 1000000000 + " seconds");
return end - start;
}
public static void main(String[] args) {
int ii;
long start, end, elapsed;
start = System.nanoTime();
System.out.println("Filling...");
int size = 1;
for (int i = 0; i < dims.length; ++i) {
size *= dims[i];
}
data = new double[size];
for (int i = 0; i < data.length; ++i) {
data[i] = Math.random();
}
array1 = new Array1(dims);
array2 = new Array2(dims);
array3 = new double[dims[0]][dims[1]][dims[2]][dims[3]];
System.out.println("Done.");
System.out.println("Writing array1...");
test(new MyRunnable() {
@Override
public void run(int ii, int i, int j, int k, int l) {
array1.set(data[ii], i, j, k, l);
}
});
System.out.println("Writing array2...");
test(new MyRunnable() {
@Override
public void run(int ii, int i, int j, int k, int l) {
array2.set(data[ii], i, j, k, l);
}
});
System.out.println("Writing array3...");
test(new MyRunnable() {
@Override
public void run(int ii, int i, int j, int k, int l) {
array3[i][j][k][l] = data[ii];
}
});
System.out.println("Reading array1...");
test(new MyRunnable() {
@Override
public void run(int ii, int i, int j, int k, int l) {
assert (data[ii] == array1.get(i, j, k, l));
}
});
System.out.println("Reading array2...");
test(new MyRunnable() {
@Override
public void run(int ii, int i, int j, int k, int l) {
assert (data[ii] == array2.get(i, j, k, l));
}
});
System.out.println("Reading array3...");
test(new MyRunnable() {
@Override
public void run(int ii, int i, int j, int k, int l) {
assert (array3[i][j][k][l] == data[ii]);
}
});
end = System.nanoTime();
elapsed = end - start;
System.out.println("Total application time is " + (double) (end - start) / 1000000000 + " seconds");
}
}
Array1 code:
package tests;
public class Array1 {
private Object delegate;
private Object allocate(Object delegate, int[] is, int pos) {
if (pos < is.length) {
delegate = new Object[is[pos]];
for (int k = 0; k < is[pos]; ++k) {
((Object[]) delegate)[k] = allocate(((Object[]) delegate)[k], is, pos + 1);
}
}
return delegate;
}
private Object get(Object delegate, int[] is, int pos) {
if (pos < is.length) {
Object subdelegate = ((Object[]) delegate)[is[pos]];
return get(subdelegate, is, pos + 1);
} else {
return delegate;
}
}
private void set(Object delegate, int[] is, int pos, double value) {
if (pos < is.length - 1) {
Object subdelegate = ((Object[]) delegate)[is[pos]];
set(subdelegate, is, pos + 1, value);
} else {
((Object[]) delegate)[is[pos]] = value;
}
}
public Array1(int... is) {
delegate = allocate(delegate, is, 0);
}
public double get(int... is) {
return (double) get(delegate, is, 0);
}
public void set(double value, int... is) {
set(delegate, is, 0, value);
}
}
Array2 code:
package tests;
public class Array2 {
private double[] delegate;
private int[] pows;
public Array2(int... is) {
pows = new int[is.length];
int size = 1;
for (int k = 0; k < is.length; ++k) {
pows[k] = size;
size *= is[k];
}
delegate = new double[size];
}
public double get(int... is) {
int pos = 0;
for (int k = 0; k < is.length; ++k) {
pos += is[k] * pows[k];
}
return delegate[pos];
}
public void set(double value, int... is) {
int pos = 0;
for (int k = 0; k < is.length; ++k) {
pos += is[k] * pows[k];
}
delegate[pos] = value;
}
}
The result: