为啥探查器看不到一种耗时的方法?

Posted

技术标签:

【中文标题】为啥探查器看不到一种耗时的方法?【英文标题】:Why doesn't profiler see one of time-consuming methods?为什么探查器看不到一种耗时的方法? 【发布时间】:2014-11-10 09:07:44 【问题描述】:

我正在尝试剖析多维数组类的两种实现思路。

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

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

很遗憾,部分时间不一致。比如 Array2 中消耗近一半时间的方法或类,在 profiler 中根本就没有列出来。

为什么?

主要代码:

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;
    


结果:

【问题讨论】:

【参考方案1】:

您似乎有第一次和第四次运行的子呼叫配置文件,第二次和第五次运行的单个呼叫配置文件,第三次和第六次运行没有呼叫;这些对应于 Array1、Array2 和内置数组的数组访问。 (您可以调用 $1.run、$2.run、$4.run 和 $5.run,但没有调用 $3.run 和 $6.run)

所以对于你的三个案例,我猜第一个没有内联调用,第二个它内联了对 runnable 的调用下面的所有内容,第三次它只看到 runnable 中的简单代码,所以它内联了下面的所有内容 test (您的打印时间总计 101.36s 与 profiler 在测试中的 101,373ms 一致)。

在对 java 进行微基准测试时,有一些常见的方法可以忽略预热结果,this answer 中有一些很好的建议。

【讨论】:

抱歉什么是“子呼叫配置文件”以及如何拥有或不拥有它们? 在您的屏幕截图中,您有显示该呼叫的配置文件(总时间/%)的树,在其中一些下,例如 $1.run,您有在该呼叫中进行的下属呼叫的配置文件,而对于 $2.run,您没有此类从属呼叫配置文件。

以上是关于为啥探查器看不到一种耗时的方法?的主要内容,如果未能解决你的问题,请参考以下文章

java,时间单独的if语句和循环而不是带有探查器的方法

当我不创建任何 char[] 实例时,为啥探查器会显示大量实例?

为啥我在探查器中获得的 C++ 运行时间不准确?

为啥链接器看不到我的函数(定义宏来替换系统日志)?

为啥 jQuery 或 getElementById 等 DOM 方法找不到元素?

为啥 jQuery 或 getElementById 等 DOM 方法找不到元素?