2

どの関数が高速です(pはMyObjectのアトミックパブリックintプロパティです):

public static boolean check(MyObject o1, List<MyObject> list) {

    int p = o1.p;
    for (int i = 0; i < 1000000; i++) {
        MyObject o = list.get(i);
        if (o.p < p) return false;
    }

    return true;
}

また

public static boolean check(MyObject o1, List<MyObject> list) {

    for (int i = 0; i < 1000000; i++) {
        MyObject o = list.get(i);
        if (o.p < o1.p) return false;
    }

    return true;
}

したがって、ローカル変数pを使用して、オブジェクトプロパティ呼び出しをキャッシュしますか、それともコンパイラの最適化によってインラインで実行されますか?

4

3 に答える 3

2

コンパイラはjavacほとんど最適化を行いません。

ただし、JIT は、フィールドをレジスタに格納するなど、コードを大幅に最適化できます。

于 2012-08-13T19:40:56.457 に答える
2

直接のトピックではありませんが、「foreach」ループを使用することで、スタイルを改善し、一貫した速度を実現できます。

for (MyObject o : list) {
    if (o.p < o1.p) return false;
}

foreach に切り替えたテストの結果:

  • ArrayList10% 遅い
  • LinkedListわずか 100000 要素で 700 倍高速
于 2012-08-13T19:47:05.630 に答える
1

短い答え: 場合によります

少し長い 回答: コンパイラ、VM、および VM の設定によって異なります。

背景: サーバー モードで HotSpot VM (最も一般的なフレーバー) を使用すると、VM がサーバー モードで不変ホイストをループするため、両方のバリアントが等しくなります。クライアントモードでは、VM が最適化の価値があると判断した場合、これ実行される場合もあれば、実行されない場合もあり、後で実行される場合もあります。

ループ不変巻き上げはループ最適化の 1 つで、最近のほとんどのコンパイラ (または Java、VM の場合) に実装されています。javac によって生成されたコードについては、VM によってさらに最適化が行われなければ、最初のコード スニペットのパフォーマンスが向上します。

public static boolean check(Test$MyObject, java.util.List);
  Code:
   0:   aload_0
   1:   getfield        #7; //Field Test$MyObject.p:I
   4:   istore_2
   5:   iconst_0
   6:   istore_3
   7:   iload_3
   8:   ldc     #4; //int 1000000
   10:  if_icmpge       42
   13:  aload_1
   14:  iload_3
   15:  invokeinterface #11,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   20:  checkcast       #5; //class Test$MyObject
   23:  astore  4
   25:  aload   4
   27:  getfield        #7; //Field Test$MyObject.p:I
   30:  iload_2
   31:  if_icmpge       36
   34:  iconst_0
   35:  ireturn
   36:  iinc    3, 1
   39:  goto    7
   42:  iconst_1
   43:  ireturn

--

public static boolean check(Test$MyObject, java.util.List);
  Code:
   0:   iconst_0
   1:   istore_2
   2:   iload_2
   3:   ldc     #4; //int 1000000
   5:   if_icmpge       38
   8:   aload_1
   9:   iload_2
   10:  invokeinterface #11,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   15:  checkcast       #5; //class Test$MyObject
   18:  astore_3
   19:  aload_3
   20:  getfield        #7; //Field Test$MyObject.p:I
   23:  aload_0
   24:  getfield        #7; //Field Test$MyObject.p:I
   27:  if_icmpge       32
   30:  iconst_0
   31:  ireturn
   32:  iinc    2, 1
   35:  goto    2
   38:  iconst_1
   39:  ireturn

ご覧のとおり、2 番目の例の 20 行目からの getfield 操作は、最初の例の 1 行目にあり、ループの外側 (バリアント 1 の 7 ~ 39 行目とバリアント 2 の 2 ~ 35 行目) にあるため、実行されるだけです。 1000000 回の代わりに ondced。

于 2012-08-15T11:03:39.067 に答える