我试图描述多维数组类的两个实现思路。
在下面的代码中,应用程序创建了三个类的巨大多维数组:Array1
,Array2
和常规。
应用程序首先创建未测量的数组。然后它填充每个数组并按顺序读取每个数组。时间测量以编程方式和分析器进行。
不幸的是,部分时间不一致。例如,消耗将近一半时间的方法或类Array2根本没有列在探查器中。
为什么?
主要代码:
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代码:
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代码:
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;
}
}
结果:
答案 0 :(得分:0)
您似乎拥有第一次和第四次运行的子呼叫配置文件,第二次和第五次运行的单呼叫配置文件,以及第三次和第六次运行没有呼叫;这些对应于Array1,Array2和内置的数组阵列访问。 (你打电话给$ 1.run,$ 2.run,$ 4.run和$ 5.run但不是$ 3.run和$ 6.run)
所以对于你的三个案例,我猜第一个没有内联调用,第二个内联调用runnable下面的所有内容,第三次它只看到runnable中的简单代码所以它内联测试下面的所有内容(打印时间101.36s的总数与测试者101,373ms的测试结果一致)。
在微观基准测试java时,有一些常见的方法可以忽略热身结果,并在this answer中提供了一些很好的建议。