为什么这种方法成为热点?

时间:2013-04-17 16:45:44

标签: java performance optimization visualvm

我正在写一个(简单的!)线性代数库。在matrix multiplication的实现中,VisualVM性能样本告诉我,当乘以大矩阵时,算法花费85%的时间(特别是“自身时间”),如下所示(5k x 120k) ):

public double next() {
    double result;

    if(hasNext())
        result = vis[i++].next();
    else
        throw new IllegalStateException("No next value");

    return result;
}

没有太多详细信息(抱歉,我无法共享更多代码),此方法是矩阵的“迭代器”的next()方法。 (您可以将此方法所在的类视为由单个列迭代器组成的行迭代器,它存储在vis中。)我很惊讶这个方法被调用很多,因为它是一个迭代器,但我感到惊讶的是,该程序花费了大量时间 这种方法。这种方法做得不多,为什么要花时间在这里呢?

以下是我要问的具体问题:

  1. 我正在点击VisualVM的某些“陷阱”吗?例如,JIT是否会以某种方式混淆VisualVM,导致VisualVM将时间归因于错误的方法?
  2. 为什么该计划会花时间在这里?该方法并没有做太多。特别是,我认为缓存效果不能解释这个问题,因为vis数组远小于乘以矩阵的数据。
  3. 如果它有用,这里是我在上面粘贴的方法的jad反汇编:

    public double next()
    {
        double result;
        if(hasNext())
    //*   0    0:aload_0         
    //*   1    1:invokevirtual   #88  <Method boolean hasNext()>
    //*   2    4:ifeq            32
            result = vis[i++].next();
    //    3    7:aload_0         
    //    4    8:getfield        #42  <Field VectorIterator[] vis>
    //    5   11:aload_0         
    //    6   12:dup             
    //    7   13:getfield        #28  <Field int i>
    //    8   16:dup_x1          
    //    9   17:iconst_1        
    //   10   18:iadd            
    //   11   19:putfield        #28  <Field int i>
    //   12   22:aaload          
    //   13   23:invokeinterface #72  <Method double VectorIterator.next()>
    //   14   28:dstore_1        
        else
    //*  15   29:goto            42
            throw new IllegalStateException("No next value");
    //   16   32:new             #89  <Class IllegalStateException>
    //   17   35:dup             
    //   18   36:ldc1            #91  <String "No next value">
    //   19   38:invokespecial   #93  <Method void IllegalStateException(String)>
    //   20   41:athrow          
        return result;
    //   21   42:dload_1         
    //   22   43:dreturn         
    }
    

    提前感谢您的帮助!

3 个答案:

答案 0 :(得分:10)

我发现这个方法看起来像一个热点,因为VisualVM被指示在其分析中忽略JRE中的方法。花在那些“被忽略”的方法上的时间(表面上)被卷入了调用栈最顶层的非忽略条目的自我时间。

以下是VisualVM中的设置屏幕,包括使数据错误的“请勿配置程序包”设置。要调整“忽略类”设置,您必须(1)单击红色突出显示的“设置”复选框,然后(2)调整以蓝色突出显示的类设置。

VisualVM Settings Screen

根据您正在做的事情,至少不要忽略java.*javax.*软件包可能是有意义的。

答案 1 :(得分:1)

我不了解VisualVM的经验。

首先确定是否检测字节码以收集统计信息。如果是这样的话,不要再犹豫了 - 检测一个简短的方法总是过度膨胀自己的时间(测量时间和增加统计计数器的成本比方法本身花费更多的时间)。

但是迭代器总是比计算本身消耗更多的时间。想象一下只是总结一个矩阵。将float值添加到局部sum变量所花费的时间比调用方法,检查不变量和最终访问数组少得多。

答案 2 :(得分:1)

忘记分析器。只需暂停一下这个糟糕的事情并检查堆栈。如果85%的时间进入该例行程序,那么每次暂停的可能性为85%,您将看到该例程中的确切位置,以及它来自何处。 您甚至可以在矩阵相乘的过程中看到它的位置。 成千上万的样本不会告诉你。

我自己的意思是调用该函数,然后执行hasNext,然后在每个元素上执行Next将比{{1}慢很多}。