5

配列を設定/返す 2 つの方法を比較してください。

static public float[] test_arr_speeds_1( int a ) {
  return new float[]{ a, a + 1, a + 2, a + 3, a + 4, a + 5,
                      a + 6, a + 7, a + 8, a + 9 };
} // or e.g. field = new float... in method

static public float[] test_arr_speeds_2( int a ) {
  float[] ret = new float[10];
  ret[0] = a;
  ret[1] = a + 1;
  ret[2] = a + 2;
  ret[3] = a + 3;
  ret[4] = a + 4;
  ret[5] = a + 5;
  ret[6] = a + 6;
  ret[7] = a + 7;
  ret[8] = a + 8;
  ret[9] = a + 9;
  return ret;
} // or e.g. field[0] = ... in method

どちらも別個のバイトコードを生成し、どちらも元の状態に逆コンパイルできます。プロファイラーを介して実行時間を確認した後 (1 億回の反復、偏りのない、異なる環境)、_1 メソッドの時間は約です。どちらも新しい配列を作成し、すべてのフィールドを特定の値に設定しますが、_2 の 4/3 の時間です。ほとんどの場合、時間は無視できますが、これはまだ私を悩ませています._1が目に見えて遅いのはなぜですか? JVM がサポートする合理的な方法で、誰かが確認/確認/説明してくれますか?

4

1 に答える 1

6

バイトコードの違いは次のとおりです(最初の2つの項目のみ)。最初の方法:

bipush  10
newarray float      //creating an array with reference on operand stack

dup
iconst_0
iload_0
i2f
fastore             //setting first element

dup
iconst_1
iload_0
iconst_1
iadd
i2f
fastore             //setting second element

//...
areturn             //returning the top of the operand stack

2番目の方法:

bipush  10
newarray float
astore_1            //creating an array and storing it in local variable

aload_1
iconst_0
iload_0
i2f
fastore             //setting first element

aload_1
iconst_1
iload_0
iconst_1
iadd
i2f
fastore             //setting second element

//...
aload_1
areturn

ご覧のとおり、唯一の違いは、最初のシナリオでは配列参照がオペランドスタックに保持されることです(そのdupため、後で配列への参照が失われるのを避けるために何度も表示されますfastore)が、2番目のシナリオでは配列参照が保持されます通常のスタック(メソッド引数とローカル変数が保持される場所)。このシナリオでは、arrayrefがオペランドスタック上にある必要があるaload_1ため、参照は常に読み取る必要があります() 。fastore

このバイトコードに基づいて仮定するべきではありません-結局のところ、それはによってCPU命令に変換され、ほとんどの場合、配列参照はCPUレジスタの1つに格納されます。そうしないと、パフォーマンスの違いが大きくなります。

違いを測定でき、低レベルの最適化を行っている場合は、より高速なバージョンを選択してください。しかし、その違いが「ポータブル」であるとは思えません(アーキテクチャーとJVMのバージョン/実装に応じて、異なるタイミング動作が観察されます)。そうは言っても、私はあなたのコンピュータでたまたま高速なバージョンではなく、もっと読みやすいバージョンを選びます。

于 2012-06-03T12:27:41.247 に答える