0

メソッドのパフォーマンスについて懸念がありました(java.util.ArrayList#add()私には遅すぎるようです)。そのため、Java ソース コードをダウンロードしてArrayList実装を調べました (問題ないようです) 。clear()add()

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 に答える