「IF - ELSE」ステートメントで置き換えるためにJavaでリフレクションまたはイントロスペクションを使用したいのですが、「リフレクション」VS「IF - ELSE」ステートメントがどのように高価であるかを知りたいですか? 約 700.000 回の繰り返しでループに使用すると、どちらがより効率的ですか?
3 に答える
ご存知かもしれませんが、条件の問題は、プロセッサの予測機能が損なわれることです。これは、プリフェッチが間違っている場合、実際には不利になることを意味します。これは、比較の結果がどうなるかわからない今日のプロセッサーで起こることです。IF-ELSE ステートメントでは、プロセッサは 50% の確率で、次に実行される命令を正しく予測します。運が良ければ、サイクルごとにより多くの命令を実行できますが、そうでない場合は、プロセッサが間違ったプリフェッチから回復するために、約 16 サイクルの中間のペナルティが発生します。
ということで、反省に移りましょう。リフレクションは意地悪です。IF パフォーマンスはあなたにとって重要です。ほとんどのフレームワークでは、リフレクションとアドレス呼び出しはC_reflection = 3*C_address_call
. Java の場合はさらに悪く、公式の数値はわかりません。最大の問題は、名前/アドレス/可用性の解決です。
とはいえ、現実世界に行って数字を見てみましょう。今では、結果を理解し、正当化し、予測することさえできます。
2 つのクラス、3 つのテストをテストします。合計 1,000 万回の呼び出し/テスト | クラスあたり 500 万回の呼び出し:
- 条件付き
- 反射
- 共通インターフェース
そして、これらは秒単位の数字です:
条件 - 0.022333
反射 - 3.02087
インターフェース - 0.012547
したがって、あなたの場合は一般的なインターフェイスが勝者であり、2番目に条件付き呼び出しが実行時間のほぼ2倍になります(上記の理由により)。最後に、非常に顕著な違いはリフレクションです。
テストのコードは次のとおりです * :
import java.lang.reflect.InvocationTargetException;
public class JavaReflectionTest {
public interface ClassInterface {
public void execute();
public String getCount();
}
public static class ClassOne implements ClassInterface {
int count = 0;
public String getCount(){
return String.valueOf(count);
}
public void execute(){
count++;
}
}
public static class ClassTwo implements ClassInterface {
int count = 0;
public String getCount(){
return String.valueOf(count);
}
public void execute(){
count++;
}
}
public static void main(String[] args) throws SecurityException, NoSuchMethodException,
IllegalArgumentException, IllegalAccessException,
InvocationTargetException, ClassNotFoundException, InterruptedException {
ClassOne one = new ClassOne();
ClassTwo two = new ClassTwo();
ClassInterface ione = new ClassOne();
ClassInterface itwo = new ClassTwo();
long stopT;
long startT;
int i;
int mod;
//Warm up
for(i=0;i<350000;i++){
one.execute();
two.execute();
ione.execute();
itwo.execute();
one.getClass().getMethod("execute").invoke(one,null);
two.getClass().getMethod("execute").invoke(two,null);
}
//Test conditional call
one = new ClassOne();
two = new ClassTwo();
Thread.sleep(1000);
startT=System.nanoTime();
for(i=0;i<10000000;i++){
mod=i%2;
if(mod==0)
one.execute();
else
two.execute();
}
stopT=System.nanoTime();
System.out.println("Conditions - " + ((stopT-startT)/1000000000.0f)+ " Calls 1: " + one.getCount() + " Calls 2: " + two.getCount());
//Test reflection
one = new ClassOne();
two = new ClassTwo();
Thread.sleep(1000);
startT = System.nanoTime();
for(i=0;i<5000000;i++){
mod=i%2;
one.getClass().getMethod("execute").invoke(one,null);
two.getClass().getMethod("execute").invoke(two,null);
mod=i%2;
}
stopT=System.nanoTime();
System.out.println("Reflection - " + ((stopT-startT)/1000000000.0f)+ " Calls 1: " + one.getCount() + " Calls 2: " + two.getCount());
//Test common interface
ione = new ClassOne();
itwo = new ClassTwo();
Thread.sleep(1000);
startT = System.nanoTime();
for(i=0;i<5000000;i++){
mod=i%2;
ione.execute();
itwo.execute();
mod=i%2;
}
stopT=System.nanoTime();
System.out.println("Interface - " + ((stopT-startT)/1000000000.0f) + " Calls 1: " + ione.getCount() + " Calls 2: " + itwo.getCount());
}
}
- パフォーマンス テストを作成する前に、コンパイラがコードをできるだけ最適化/変更しないことを確認する必要があります。これは、直感的ではないかもしれないテストのステートメントのいくつかを説明しています。