为什么分析器不会看到耗时的方法之一?

时间:2014-11-10 09:07:45

标签: java multidimensional-array profiling visualvm

我试图描述多维数组类的两个实现思路。

在下面的代码中,应用程序创建了三个类的巨大多维数组:Array1Array2和常规。

应用程序首先创建未测量的数组。然后它填充每个数组并按顺序读取每个数组。时间测量以编程方式和分析器进行。

不幸的是,部分时间不一致。例如,消耗将近一半时间的方法或类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;
    }

}

结果:

enter image description here

1 个答案:

答案 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中提供了一些很好的建议。