Jvm 未使用的变量真的会影响JIT优化吗?
下面是[Java performance]中的一个示例:Jvm 未使用的变量真的会影响JIT优化吗?,jvm,jvm-hotspot,Jvm,Jvm Hotspot,下面是[Java performance]中的一个示例: public static void main(String[] args){ double d; long start=System.currentTimeMillis(); for (int i=0;i<10;i++){ d=fblImpl1(50); } long end=System.currentTimeMillis
public static void main(String[] args){
double d;
long start=System.currentTimeMillis();
for (int i=0;i<10;i++){
d=fblImpl1(50);
}
long end=System.currentTimeMillis();
System.out.println("elapsed time :"+(end-start));
}
private static double fblImpl1(int n){
if(n<0) throw new IllegalArgumentException("Must be >0");
if (n==0) return 0d;
if (n==1) return 1d;
double d =fblImpl1(n-2) +fblImpl1(n-1);
if (Double.isInfinite(d)) throw new ArithmeticException("overflow");
return d;
}
因为程序不使用下面的变量“d”。
但是在我的测试中,OracleHostSpotJDK1.7没有进行这种优化,程序需要运行很长时间才能完成
@霍尔格,首先谢谢你的回复。根据我对你的话的理解,我修改了代码如下:
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
doTest();
}
}
private static void doTest() {
double d;
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
d = fblImpl1(50);
}
long end = System.currentTimeMillis();
System.out.println("elapsed time :" + (end - start));
}
private static double fblImpl1(int n) {
//if (n < 0) throw new IllegalArgumentException("Must be >0");
if (n == 0) return 0d;
if (n == 1) return 1d;
double d = fblImpl1(n - 2) + fblImpl1(n - 1);
//if (Double.isInfinite(d)) throw new ArithmeticException("overflow");
return d;
}
publicstaticvoidmain(字符串[]args){
对于(int i=0;i<10000;i++){
doTest();
}
}
私有静态void doTest(){
双d;
长启动=System.currentTimeMillis();
对于(int i=0;i<10000;i++){
d=fblImpl1(50);
}
long end=System.currentTimeMillis();
System.out.println(“运行时间:+(结束-开始));
}
专用静态双fblImpl1(整数n){
//如果(n<0)抛出新的IllegalArgumentException(“必须大于0”);
如果(n==0)返回0d;
如果(n==1)返回1d;
双d=fblImpl1(n-2)+fblImpl1(n-1);
//if(Double.isInfinite(d))抛出新的算术异常(“溢出”);
返回d;
}
}
但JIT似乎仍然没有像我预期的那样应用任何优化。有什么问题吗?您不需要多次执行main方法,因为JIT可以编译经常执行的循环,所以不需要“doTest”方法。问题似乎出在递归中。如果将fblImpl1替换为sum之类的琐碎内容,则很容易获得所需的效果
public static void main(String[] args) {
double d;
List<Long> list = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
long start = System.nanoTime();
fblImpl1(20000);
long end = System.nanoTime();
list.add(end - start);
}
for(long z : list) {
System.out.println(z);
}
}
public static double fblImpl1(int n) {
int sum = 0;
for(int i = 0; i < n; i++) {
sum += i;
}
return sum;
}
提示:不要使用SystemCurrentTimeMillis,因为它的精度非常差(特别是在Windows上)因为代码可能会抛出异常,JIT必须证明
n
永远不会是负的,d
永远不会是无限的。除此之外,只有当main
方法本身被频繁调用时,这种优化才能被预期。否则,很可能优化器的范围太窄,无法完全省略优化。如果它不看main
方法,而只看递归的fblImpl1
调用,它就不会意识到您在用相同的输入重复调用它。@Holger,我没有测量doTest的所有循环,但它确实花了很多时间。将循环放在main方法中意味着依赖于堆栈替换。如果有效,很好,但如果无效,则无法证明缺少优化表示总体不可用,因为OSR也可能失败。因此,当优化没有发生时,总是值得尝试使用称为多次的单独方法。虽然递归是问题所在,但测试并不能证明死代码消除已经发生。优化器可以将循环转化为一次乘法,这将产生相同的效果。@Holger你说得对。因此,证明代码被删除的唯一方法似乎是查看汇编代码内部,对吗?除非您找到优化器绝对不能缩短的操作,是的,这是唯一的方法。
public static void main(String[] args) {
double d;
List<Long> list = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
long start = System.nanoTime();
fblImpl1(20000);
long end = System.nanoTime();
list.add(end - start);
}
for(long z : list) {
System.out.println(z);
}
}
public static double fblImpl1(int n) {
int sum = 0;
for(int i = 0; i < n; i++) {
sum += i;
}
return sum;
}
-XX:+PrintCompilation -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining