9

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

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数组比被相乘的矩阵的数据要小得多。

如果它有用,这是我上面粘贴的方法的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         
}

提前感谢您的帮助!

4

3 回答 3

11

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

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

VisualVM 设置屏幕

根据你在做什么,至少不要忽略java.*andjavax.*包可能是有意义的。

于 2013-04-19T21:30:01.750 回答
1

忘记分析器。只需暂停这该死的东西几次并检查堆栈。如果 85% 的时间进入该例程,那么在每次暂停时,您将有 85% 的机会准确地看到它在该例程中的位置,以及它的确切来源。您甚至可以看到它在矩阵相乘过程中的位置。成千上万的样本不会告诉你这一点。

我自己的感觉是调用该函数,然后执行hasNext,然后Next每个元素执行操作将比i++.

于 2013-04-17T22:04:55.630 回答
1

我从经验中不了解 VisualVM。

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

但是迭代器总是有可能比计算本身消耗更多的时间。想象一下,只是总结一个矩阵。与调用方法、检查不变量并最终访问数组相比,将浮点值添加到局部 sum 变量所需的时间要少得多。

于 2013-04-17T17:15:01.227 回答