JVM优化之循环展开(附有详细的汇编代码)

本文探讨了JVM中的循环展开优化技术,通过实例展示了HotSpot JIT编译器如何通过循环展开减少前向跳转,提高循环执行效率。通过对C和Java代码的对比,以及汇编代码的分析,揭示了Java在循环优化方面的优势,指出使用int计数的循环相比long计数的循环能获得更高的性能。
摘要由CSDN通过智能技术生成

在JVM内部实现系列的前几篇文章中,我们已经看到了Java的HotSpot虚拟机的just-in-time (JIT)编译技术,包括逃逸分析和锁消除。本文我们将要讨论另一种自动优化,叫作循环展开。JIT编译器使用这项技术来让循环(比如Java的for或者while循环)执行得更加高效。

由于我们要对JVM的内部机制进行深入分析,所以你会时不时看到用于讲解介绍的各种C的代码甚至是汇编语言,扶稳了!

我们先从下面这段C代码开始,它会去分配100万个long类型的空间,然后用100万个随机的long值来填充。

int main(int argv, char** argc) {
int MAX = 1000000;
long* data = (long*)calloc(MAX, sizeof(long));
for (int i = 0; i < MAX; i++) {
data[i] = randomLong();
}
}
C被认为是一门高级语言,不过事实真的是这样的吗?在苹果Mac电脑上,用Clang编译器(开启—S选项来打印Intel格式的汇编语言)来编译前面的代码会得到如下的输出结果:

_main: ## @main

BB#0:

pushq   %rbp
movq    %rsp, %rbp
subq    $48, %rsp
movl    $8, %eax
movl    %eax, %ecx
movl    $0, -4(%rbp)
movl    %edi, -8(%rbp)
movq    %rsi, -16(%rbp)
movl    $1000000, -20(%rbp)   ## imm = 0xF4240
movslq  -20(%rbp), %rdi
movq    %rcx, %rsi
callq   _calloc
movq    %rax, -32(%rbp)
movl    $0, -36(%rbp)

LBB1_1: ## LBB1_1是内部循环的Header Depth=1
movl -36(%rbp), %eax
cmpl -20(%rbp), %eax
jge LBB1_4

BB#2: ## 循环体内部: Header=BB1_1 Depth=1

callq   _randomLong
movslq  -36(%rbp), %rcx
movq    -32(%rbp), %rdx
movq    %rax, (%rdx,%rcx,8)

BB#3: ## 循环体内部: Header=BB1_1 Depth=1

movl    -36(%rbp), %eax
addl    $1, %eax
movl    %eax, -36(%rbp)
jmp LBB1_1

LBB1_4:
movl -4(%rbp), %eax
addq $48, %rsp
popq %rbp
retq
看下这个代码,你会发现开始处有一次calloc函数的调用,并且仅存在一次randomLong()函数的调用(在循环中)。里面有两次跳转,它和下面变种的C代码所生成的机器代码本质上是一样的:

int main(int argv, char** argc) {
int MAX = 1_000_000;
long* data = (long*)calloc(MAX, sizeof(long));
int i = 0;
LOOP: if (i >= MAX)
goto END;
data[i] = randomLong();
++i;
goto LOOP;
END: return 0;
}
Java里面同样的代码应该是这样的:

public class LoopUnroll {
public static void main(String[] args) {
int MAX = 1000000;
long[] data = new long[MAX];
java.util.Random random = new java.util.Random();
for (int i = 0; i < MAX; i++) {
data[i] = random.nextLong();
}
}
}
编译成字节码的话,就成了这样:

public static void main(java.lang.String[]);
Code:
0: ldc #2 // int 1000000
2: istore_1
3: iload_1
4: newarray long
6: astore_2
7: new #3 // class java/util/Random
10: dup
11: invokespecial #4 // 方法 java/util/Random."😦)V
14: astore_3
15: iconst_0
16: istore 4
18: iload 4
20: iload_1
21: if_icmpge 38
24: aload_2
25: iload 4
27: aload_3
28: invokevirtual #5. // 方法 java/util/Random.nextLong:()J
31: lastore
32: iinc 4, 1
35: goto 18
38: return
这些程序在代码结构来看都非常相似。它们都在循环中对数组data进行了一次操作。真实的处理器会有指令流水线(instruction pipeline),如果程序一直向下线性执行的话,就能够充分地引用流水线,因为下一条执行的指令马上就会就绪。

不过,一旦碰到跳转指令,指令流水线

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值