我想在java中使用反射或内省来替换“IF - ELSE”语句,我想知道“反射”VS“IF - ELSE”语句有多昂贵?如果我使用大约 700.000 次迭代的循环,哪个更有效?
3 回答
参见 Effective Java,第 53 条:首选接口而不是反射
性能受到影响。反射方法调用比普通方法调用慢得多。到底慢了多少很难说,因为有很多因素在起作用。在我的机器上,速度差异可以小到两倍或大到 50 倍。
您可能知道,条件的问题在于它们削弱了处理器的可预测性功能。这意味着如果预取是错误的,它实际上会变成一个劣势。这就是当今处理器在不知道比较结果可能是什么时发生的情况,因此他们打赌。在 IF-ELSE 语句中,处理器有 50% 的概率正确预测要执行的下一条指令。如果幸运的话,我们每个周期执行更多指令,否则,我们会得到大约 16 个周期的中等惩罚,以便处理器从错误的预取中恢复。
也就是说,让我们继续反思。反思是卑鄙的,中频表现对你很重要。在大多数框架中,反射与地址调用是C_reflection = 3*C_address_call
. 在 Java 中情况更糟,我并没有关于它的官方数据。最大的问题是名称/地址/可用性解析。
也就是说,让我们去现实世界看看一些数字。正如我们现在可以理解、证明甚至预测结果一样。
我们测试 2 个类,3 个测试。总共 1000 万次调用/测试 | 每堂课 500 万次调用:
- 有条件的
- 反射
- 通用接口
这些是以秒为单位的数字:
条件 - 0.022333
反射 - 3.02087
接口 - 0.012547
因此,在您的情况下,通用接口是赢家,其次是执行时间几乎翻倍的条件调用(出于上述原因)。最后一个非常显着的区别是反射。
这是测试的代码 * :
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());
}
}
- 在创建性能测试之前,我们需要确保编译器将尽可能少地优化/修改我们的代码,这解释了测试中的一些您可能不直观的语句。