0

我试图弄清楚我在代码中创建对象数组的方式是否有问题,或者在 Java 中处理对象数组是否比我最初认为的更昂贵。

我有一个名为 的简单枚举类ElementaryTransitionTerm,它只包含两个原始成员变量。另一个类中的方法创建一个由该 Enum 的常量组合组成的数组(数组的可能长度永远不会超过 3)。

问题在于,程序在所述方法中总共花费(平均)13ms,尽管它只被调用了 8 次。我不知道为什么。

运行时间通过两种方式计算:

  1. 通过 System.nanoTime() 调用围绕对方法的调用。

  2. 通过 System.nanoTime() 调用围绕方法内的每个数组创建语句并将它们总结起来

这是ElementaryTransitionTerm枚举类:

public enum ElementaryTransitionTerm 
{
    MATCH(1, 0), INSERTION(0, 1), SUBSTITUTION(1, 1), DELETION(0, 0), FAILURE(0, 0);

    private final int I_OFFSET;
    private final int E_OFFSET;


    private ElementaryTransitionTerm(int iOffset, int eOffset)
    {
        I_OFFSET = iOffset;
        E_OFFSET = eOffset;

    }
}

这是有问题的方法createTransitionTerms(以及用作参数的枚举类型的声明):

//'E' and 'I' are both private int fields in the parent class.

private enum RelevantSubwordHitIndexType { FIRST_INDEX, TRAILING_INDEX, NO_INDEX };

/**
 * Creates an array of ElementaryTransitionTerm constants.

 * @param hitIndexType     a RelevantSubwordHitIndexType enum constant
 */
private ElementaryTransitionTerm[] createTransitionTerms(RelevantSubwordHitIndexType hitIndexType)
{
    //Simple getter method chained in to String.length() call
    int onePastFinalBoundary = parentAutomaton.getAutomatonString().length(); 

    if(E < parentAutomaton.getMaxEditDistance())   //Simple getter method retrieving an int
    {
        if(I < onePastFinalBoundary - 1)           
        {
            switch(hitIndexType)
            {
                case FIRST_INDEX:       return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.MATCH};
                case TRAILING_INDEX:    return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION, ElementaryTransitionTerm.SUBSTITUTION, ElementaryTransitionTerm.DELETION};
                default:                return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION, ElementaryTransitionTerm.SUBSTITUTION};
            }
        }
        else if(I == onePastFinalBoundary - 1)     
        {
            switch(hitIndexType)
            {
                case FIRST_INDEX:       return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.MATCH};
                default:                return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION, ElementaryTransitionTerm.SUBSTITUTION};
            }
        }
        else                            return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION};     
    else                                            
    {
        if(I < onePastFinalBoundary)                
        {
            switch(hitIndexType)
            {
                case FIRST_INDEX:       return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.MATCH};
                default:                return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.FAILURE};
            }
        }
        else                            return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.FAILURE};
    }
}

该方法本质上评估一小组条件以创建一个小数组。但是,此方法运行 8 次的总执行时间并不能反映这一点。

我什至public final在 ElementaryTransitionTerm 中将要返回的数组声明为数组,并将上述方法中的创建语句替换为对这些数组的引用,但这对代码的运行时间完全没有影响。

我编写的代码可以在更短的时间内完成更多工作。所以我很好奇为什么这种方法会这样。有没有人有任何想法?

编辑

经过进一步测试,我发现了两件事:

  1. 单独测试该方法会产生预期的(即亚毫秒)结果。
  2. 在程序中测试该方法,似乎在第一次调用该函数时执行的数组创建语句基本上对我所看到的时间负责。所有后续调用都在亚毫秒时间内完成。

关于#2:这是什么原因,我该如何纠正它?

4

3 回答 3

2

为了完成 Shivan 的回答,我用随机生成的输入(一百万次)运行了一个测试,我得到了 40 到 50 毫秒之间的结果。

这是随机生成部分:

public MyTest() {
    Random r = new Random();

    parentAutomaton = new ParentAutomaton();
    parentAutomaton.maxEditDistance = r.nextInt(255);
    parentAutomaton.automatonStringLen = r.nextInt(255);
    E = r.nextInt(255);
    I = r.nextInt(255);
    relevantSubwordHitIndexType = RelevantSubwordHitIndexType.values()[r.nextInt(3)];

}

和执行方法:

public static void main(String [] args) {
    List<MyTest> l = new ArrayList<MyTest>();

    for (int i = 0; i < 1000000; i++) {
         l.add(new MyTest());
    }

    long start = System.nanoTime();
    for (MyTest t:l) {
        t.createTransitionTerms(t.relevantSubwordHitIndexType);
    }

    double dur_nano = System.nanoTime() - start;
    double dur_mili = dur_nano / 1000000.0;
    System.out.println("Finished in : " + dur_mili);

}

我的操作系统是Ubuntu 12.04,我的 CPU 是Intel(R) Core(TM)2 Duo CPU T7250 @ 2.00GHz 和 2GB 内存。我用 Java 1.6.0_24 运行它。

如果您想要完整的源代码,请告诉我。

希望能帮助到你,

于 2012-08-25T18:57:24.033 回答
2

运行您的代码,我得到 25-30 毫秒的时间来调用该方法一百万次。在 Sony Vaio Z、i5、4 GB Ram、Win 7 64 但 JDK 6 64 位上运行。

这是我尝试过的实际代码:

    private ElementaryTransitionTerm[] createTransitionTerms(RelevantSubwordHitIndexType hitIndexType) {
            //Simple getter method chained in to String.length() call
            int onePastFinalBoundary = "A".length(); 

//Here I also tried if(1 < 2) to change the if/else execution path, same run time for each.
            if(3 < 2)    {
                if(1 < 2) {
                    switch(hitIndexType) {
                        case FIRST_INDEX:       return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.MATCH};
                        case TRAILING_INDEX:    return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION, ElementaryTransitionTerm.SUBSTITUTION, ElementaryTransitionTerm.DELETION};
                        default:                return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION, ElementaryTransitionTerm.SUBSTITUTION};
                    }
                } else if(1 == 1) {
                    switch(hitIndexType) {
                        case FIRST_INDEX:       return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.MATCH};
                        default:                return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION, ElementaryTransitionTerm.SUBSTITUTION};
                    }
                }
                else {                     
                    return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.INSERTION};
                }
            } else  {
                if(1 < 2) {
                    switch(hitIndexType) {
                        case FIRST_INDEX:       return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.MATCH};
                        default:                return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.FAILURE};
                    }
                } else {
                    return new ElementaryTransitionTerm[] {ElementaryTransitionTerm.FAILURE};
                }
            }
        }

        public static void main(String args[]) {
            T1 t = new T1();
            long st = System.currentTimeMillis();
            for(int i = 0 ; i<1000000; i++) {
                t.createTransitionTerms(RelevantSubwordHitIndexType.FIRST_INDEX);
            }
            long et = System.currentTimeMillis();
            System.out.println(et-st);
        }

您能告诉我们您测试的是什么硬件、操作系统、JDK 版本吗?

于 2012-08-25T17:07:56.563 回答
-1

尝试以下想法可能值得尝试:

  1. 预先创建一些数组,例如return new ElementaryTransitionTerm[] { ElementaryTransitionTerm.INSERTION };并将它们保留为private static值。
  2. 重新排序您的案例和条件,以便如果有一个分支比其他分支更常执行,那么以某种方式将其放在第一位。
于 2012-08-25T17:08:21.740 回答