/**
* 生成一个没有重复元素的整型数组
*/
private static int[] createArray(final int length) {
int[] a = new int[length];
Set<Integer> set = new HashSet<Integer>();
Random rand = new Random();
for(int i=0, x=-1; i<a.length;) {
x = rand.nextInt(length*5); //这里编译器会不会优化?
if(set.add(x) == true) {
a[i++] = x;
}
}
return a;
}
在上述代码中,"length*5"这一乘法操作被写到了循环中,如果每次都做乘法,效率显然比较低下
我在声明函数的时候,特地把length声明为final的,请问编译器会优化吗?
* 生成一个没有重复元素的整型数组
*/
private static int[] createArray(final int length) {
int[] a = new int[length];
Set<Integer> set = new HashSet<Integer>();
Random rand = new Random();
for(int i=0, x=-1; i<a.length;) {
x = rand.nextInt(length*5); //这里编译器会不会优化?
if(set.add(x) == true) {
a[i++] = x;
}
}
return a;
}
在上述代码中,"length*5"这一乘法操作被写到了循环中,如果每次都做乘法,效率显然比较低下
我在声明函数的时候,特地把length声明为final的,请问编译器会优化吗?
int[] a = new int[length];
int len = 5 * length;
Set<Integer> set = new HashSet<Integer>();
Random rand = new Random();
for(int i=0, x=-1; i<a.length;) {
x = rand.nextInt( len ); //这里编译器会不会优化?
if(set.add(x) == true) {
a[i++] = x;
}
}
return a;
}这么写不就得了?另外这里编辑器没法优化。
很难讲。(有也是运行时的优化,不可能是编译时的优化。)但是多写一行,不是问题。
编译器连循环都能给你展开了,替换个常量毫无难度啊传说中的循环展开:
for(int i=0; i<3; i++)
a[i] = i;//据说为了提高效率,编译器会把它变成
a[0]=0;
a[1]=1;
a[2]=2;
//这样就不用分支预测了
第一次运行,可能出现替换。
而且也不行啊,这个函数不知道被哪些地方调用了,length并非总是一个固定值啊。可能是3, 可能是4。所以里面的代码也没有办法真正替换的。
下面是方法的字节码
public static int[] createArray(int);
Code:
0: iload_0
1: newarray int
3: astore_1
4: new #16; //class java/util/HashSet
7: dup //
8: invokespecial #18; //Method java/util/HashSet."<init>":()V
11: astore_2
12: new #19; //class java/util/Random
15: dup
16: invokespecial #21; //Method java/util/Random."<init>":()V
19: astore_3
20: iconst_0
21: istore 4
23: iconst_m1
24: istore 5
26: goto 61
29: aload_3
30: iload_0
31: iconst_5
32: imul
33: invokevirtual #22; //Method java/util/Random.nextInt:(I)I
36: istore 5
38: aload_2
39: iload 5
41: invokestatic #26; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
44: invokeinterface #32, 2; //InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
49: ifeq 61
52: aload_1
53: iload 4
55: iinc 4, 1
58: iload 5
60: iastore
61: iload 4
63: aload_1
64: arraylength
65: if_icmplt 29
68: aload_1
69: areturn
下面是两个方法: public void m1(final int i){
System.out.println(i*7);
}
public void m2(int i){
System.out.println(i*7);
}
编译后的字节码如下:public void m1(int);
Code:
0: getstatic #39; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iload_1
4: bipush 7
6: imul
7: invokevirtual #58; //Method java/io/PrintStream.println:(I)V
10: returnpublic void m2(int);
Code:
0: getstatic #39; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iload_1
4: bipush 7
6: imul
7: invokevirtual #58; //Method java/io/PrintStream.println:(I)V
10: return
很容易看出来两者是一样的,并没有把final考虑进去
听说数据库里加了 not null 限制的字段,排序速度会有提升
java为啥不考虑优化呢?
public void m1(){
final int total = 7; System.out.println(total * 5);
}
编译后的字节码如下:public void m1();
Code:
0: bipush 7
2: istore_1
3: getstatic #39; //Field java/lang/System.out:Ljava/io/PrintStream;
6: bipush 35
8: invokevirtual #57; //Method java/io/PrintStream.println:(I)V
11: return你的length在编译期的值不可确定,所以编译器无法做出优化处理了
你听说过Java算斐波那契数列比C++快的传说没? JVM会缓存一些中间结果,使得有些重复过的递归调用变成了查表。而C++=>ASM就做不到。但是Java依赖聪明的JVM就行了。
但是你编译出来的JVM的byte code,估计你看不到编译出来的 查表/存表 这类的代码。为什么?优化不仅仅体现在字节码上。Java的Hotspot中的Profile Monitor就是干这个的。如果length * 5被多次调用了,也许它就会出来优化一下。
【以上都是我合理猜测了,我是没用证据的】
就算你的猜测都是合理的,但是除了jvm的设计实现者之外没有人清楚底层是如何运行的,是不是?
我们可以控制的也就是java源码和jvm字节码而已,所以我们提到的“编译器优化”也就只能到这一层面而已。你能通过你的源码去控制jvm运行时的优化么?
最后,你的第一句说:“就算”从心理学上说,你开始接受我一部分观点了,但是还在嘴硬
但是如何仅仅是基于有限的jvm虚拟机知识的"猜测", 实在是不足以信服.没有实践,就没有发言权,你说能写出利于jvm优化的代码,就请给出个简单的例子说明,否则就是在空谈!最后,我说"就算",是因为你说的关于jvm的理论我不能确定对错,我不能妄下判断而已. 这和嘴硬不硬没有关系.