0
class DummyInteger {
    private int i;
    public DummyInteger(int i) {
        this.i = i;
    }
    public int getI() {
        return i;
    }
}

long start = System.nanoTime();
DummyInteger n = new DummyInteger(10);
long end = System.nanoTime();
long duration = end - start;
System.out.println(duration);

前のコードは、次の出力を生成します。

341000

一方:

long start = System.nanoTime();
ArrayList a = new ArrayList();
long end = System.nanoTime();
long duration = end - start;
System.out.println(duration);

次の出力が生成されます。

17000

さて、私の質問は、クラスによって行われる作業がコンストラクターDummyIntegerによって行われる作業と同じくらいであるように見えるにもかかわらず、なぜ実行時間にそのような違いが観察されるのでしょうか? ArrayListプリコンパイルされている ArrayList のコードと関係がありますか? それとも、処理時間に影響を与えている他の要因ですか?

ありがとうございました。

- 編集 -

2つの異なるタイプのオブジェクトを比較するという問題が発生すると思いましたが、次のコードを使用しても、次のようなオブジェクトを作成する場合と比較してArrayList:

class IntList {
    int [] elementData;

    public IntList() {
        elementData = new int [20];
    }
}

long start = System.nanoTime();
IntList n = new IntList();
long end = System.nanoTime();
long duration = end - start;
System.out.println(duration);

結果は同じですが、この場合、特定のチェックが実行されるため、 の作成によるオーバーヘッドArrayListが大きくなるはずであり、ソース コードを調べることで見つけることができます。

もう 1 つ注意すべき点は、2 つの異なる実行で両方のコードを実行することです。これにより、JVM の初期化に起因する可能性のあるオーバーヘッドが排除されます。

4

4 に答える 4

8

なぜ実行時間にこのような違いが見られるのでしょうか?

あなたの測定は無意味なので:

  • それは欠陥のあるマイクロベンチマークです(JVMウォームアップはありません。それを実行するだけでは、JVMの起動もおそらく測定に干渉します...)=>マイクロベンチマークを作成するにはどうすればよいですか?
  • の解像度は、nanotime測定している操作には低すぎます。おそらく数ナノ秒かかりますが、nanotime解像度は 1 ms に近いです。

より堅牢な方法に従って得た結果は次のとおりです。新しい Integer の作成には約 6 ナノ秒かかりますが、私のマシンではデフォルト サイズ (10) の ArrayList の作成には約 19 ナノ秒かかります。

DummyInteger の作成:

Run result "newInteger": 6.064 ±(95%) 0.101 ±(99%) 0.167 nsec/op
Run statistics "newInteger": min = 6.007, avg = 6.064, max = 6.200, stdev = 0.081
Run confidence intervals "newInteger": 95% [5.964, 6.165], 99% [5.897, 6.231]

リストの作成:

Run result "newList": 19.139 ±(95%) 0.192 ±(99%) 0.318 nsec/op
Run statistics "newList": min = 18.866, avg = 19.139, max = 19.234, stdev = 0.155
Run confidence intervals "newList": 95% [18.948, 19.331], 99% [18.821, 19.458]

編集

私の「より堅牢な方法論」にも欠陥があり、作成は生意気な JIT によって実際に最適化されました... 結論は似ていますが、上記の新しい結果: これらは非常に高速な操作です。

于 2013-05-18T08:09:16.340 に答える
4

クラスのロードは、実行できる最もコストのかかる作業の 1 つです。(特に、あまり機能しないクラスの場合) 組み込みクラスの多くは、プログラムの開始前に使用されるため、再度ロードする必要はありません。クラスを使用するにつれて、そのコードはウォームアップされます。

あなたが言及した3つのクラスが繰り返し作成される次の例を考えてみましょう

static class DummyInteger {
    private int i;

    public DummyInteger(int i) {
        this.i = i;
    }

    public int getI() {
        return i;
    }
}

static class IntList {
    int[] elementData;

    public IntList() {
        elementData = new int[20];
    }
}


public static void main(String... ignored) {
    timeEach("First time", 1);
    for (int i = 1000; i <= 5000; i += 1000)
        timeEach(i + " avg", i);
    for (int i = 10000; i <= 20000; i += 10000)
        timeEach(i + " avg", i);
}

public static void timeEach(String desc, int repeats) {
    long time1 = System.nanoTime();
    for (int i = 0; i < repeats; i++) {
        List l = new ArrayList();
    }
    long time2 = System.nanoTime();
    for (int i = 0; i < repeats; i++) {
        DummyInteger di = new DummyInteger(i);
    }
    long time3 = System.nanoTime();
    for (int i = 0; i < repeats; i++) {
        IntList il = new IntList();
    }
    long time4 = System.nanoTime();
    System.out.printf("%s: ArrayList %,d; DummyInteger %,d; IntList %,d%n",
            desc, (time2 - time1) / repeats, (time3 - time2) / repeats, (time4 - time3) / repeats);
}

Java 7 update 21 で出力され、-XX:+PrintCompilation

     89    1             java.lang.String::hashCode (55 bytes)
     89    2             java.lang.String::charAt (29 bytes)
First time: ArrayList 41,463; DummyInteger 422,837; IntList 334,986
1000 avg: ArrayList 268; DummyInteger 60; IntList 136
    120    3             java.lang.Object::<init> (1 bytes)
2000 avg: ArrayList 321; DummyInteger 75; IntList 142
3000 avg: ArrayList 293; DummyInteger 63; IntList 133
    123    4             Main::timeEach (152 bytes)
    124    5             java.util.AbstractCollection::<init> (5 bytes)
    124    6             java.util.AbstractList::<init> (10 bytes)
    125    7             java.util.ArrayList::<init> (44 bytes)
4000 avg: ArrayList 309; DummyInteger 64; IntList 175
    126    8             java.util.ArrayList::<init> (7 bytes)
    127    9             Main$DummyInteger::<init> (10 bytes)
    127   10             Main$IntList::<init> (13 bytes)
5000 avg: ArrayList 162; DummyInteger 70; IntList 149
10000 avg: ArrayList 0; DummyInteger 0; IntList 0
20000 avg: ArrayList 0; DummyInteger 0; IntList 0

パフォーマンスが向上するにつれて、ArrayList が最も遅いことがわかります。最後に、JIT はオブジェクトが使用されておらず、作成する必要がないと判断します。その後、ループは空で実行する必要がないため、平均時間は 0 になります。

于 2013-05-18T08:26:36.883 に答える
1

さて、私の質問は、DummyInteger クラスによって行われる作業が ArrayList コンストラクターによって行われる作業と同じくらいであるように見えるにもかかわらず、実行時間にこのような違いが見られるのはなぜでしょうか?

このベンチマークは悪いからです。1 回の実行からは何もわかりません。

プリコンパイルされている ArrayList のコードと関係がありますか?

いいえ。

于 2013-05-18T08:09:30.060 に答える
0

2 つの異なるタイプのオブジェクト、つまり arraylist と Your Dummyobject の時間パフォーマンスを比較しているため、時間統計が異なります。さて、あなたの質問に行きましょう

事前実装された Java クラスのオブジェクトを作成すると、カスタム オブジェクトを作成するよりもはるかに高速ですか?

事前に実装されたJavaも一種のカスタムオブジェクトですが、他の誰かがあなたのために作成したため、正しいステートメントではありません(あなたが自分でカスタムオブジェクトを作成するように)。したがって、事前に実装されたJavaクラスとカスタムオブジェクトではありませんが、実際には、オブジェクトの作成中に行われる操作に依存します。

于 2013-05-18T07:55:19.023 に答える