0

我对java.util.ArrayList#add()方法的性能有些担心(对我来说似乎太慢了),所以我下载了 Java 源代码并查看了ArrayList实现(看起来不错),我复制了方法clear()add()创建了自己的 ArrayList2:

public class ArrayList2<E> 
{
    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};
    static int MAX_ARRAY_SIZE = 50000;

    private transient Object[] elementData;

    private int size;

    public ArrayList2(int initialCapacity) {
        super();
        if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0)
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }    

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {

        if (minCapacity - elementData.length > 0){
            //System.out.println("WHAT");  //when this line is uncommented performance is improved
            grow(minCapacity);
        }
    }

    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    public void clear() {

        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }
}

这当然与java.util.ArrayList. 未注释第 49 行时会发生奇怪的事情。它包含这个:

System.out.println("What");

在此更改之后,add 方法的运行速度提高了 3-4 倍,这很奇怪,因为该行位于一个从未执行过的代码块中,因此应该不会影响性能。我使用了一个简单的测试来测量不同方法所花费的时间。这是输出:

**************************************
Filling Array List 2 took : 2107
Emptying Array List 2 took : 149
**************************************
**************************************
Filling Array List 3 took : 565
Emptying Array List 3 took : 182
**************************************

这种逻辑上不相关的变化如何能如此显着地提高性能?有人可以解释一下吗?这对我来说真的没有任何意义,目前在我看来是 java.util.ArrayList 中的一个大性能问题。

你可以在这里下载源代码:

http://goo.gl/62Ri5T

您可以使用以下 maven 命令运行它:

mvn exec:java -Dexec.mainClass="Test" 
4

2 回答 2

4

正如评论中提到的,这似乎是一个不恰当的微基准。

您会看到,即使 if 语句的内部部分可能不会在您的特定情况下执行,该方法本身也会被频繁调用。现在,由于对 System.out 的内部调用,方法字节码更长,因此 JIT 可能不太愿意优化该方法(“短”方法很快就会被内联;对 System.out 的调用可能会降低它的趣味性比其他地方的一些其他方法进行优化)。

因此,如果第一个解决方案更早地得到优化,为什么它在您的测试中会变慢?因为 JIT 编译是异步执行的,而且很耗时。因此,在进行优化时,该方法会变慢,直到完全优化。然后它变得非常快。只需使用参数“-XX:-PrintCompilation”执行 Java 即可。

但无论如何......使用卡尺。真的。

于 2013-10-17T02:21:34.933 回答
2

现在我确定......它的基准很糟糕。试着重复 10 次。第一个结果:

**************************************
Filling Array List 2 took  : 1754
Emptying Array List 2 took : 191
**************************************
**************************************
Filling Array List 3 took  : 534
Emptying Array List 3 took : 188
**************************************

没有将计时器归零的最后结果,即显示累积时间:

**************************************
Filling Array List 2 took  : 18647
Emptying Array List 2 took : 1945
**************************************
**************************************
Filling Array List 3 took  : 17310
Emptying Array List 3 took : 1865
**************************************

将计时器归零的最后结果,即显示最后一次运行的时间:

**************************************
Filling Array List 2 took  : 1733
Emptying Array List 2 took : 179
**************************************
**************************************
Filling Array List 3 took  : 1897
Emptying Array List 3 took : 184
**************************************

你可以看到这些数字没有意义,对吧?只需切换到卡尺

于 2013-10-17T02:11:58.960 回答