gpt4 book ai didi

java - JIT 未优化涉及 Integer.MAX_VALUE 的循环

转载 作者:IT老高 更新时间:2023-10-28 20:32:49 27 4
gpt4 key购买 nike

在写 another question 的答案时,我注意到 JIT 优化的一个奇怪的边界情况。

以下程序不是“微基准”并且不是旨在可靠地测量执行时间(如另一个问题的答案中所指出的)。它仅用作 MCVE重现问题:

class MissedLoopOptimization
{
public static void main(String args[])
{
for (int j=0; j<3; j++)
{
for (int i=0; i<5; i++)
{
long before = System.nanoTime();
runWithMaxValue();
long after = System.nanoTime();
System.out.println("With MAX_VALUE : "+(after-before)/1e6);
}
for (int i=0; i<5; i++)
{
long before = System.nanoTime();
runWithMaxValueMinusOne();
long after = System.nanoTime();
System.out.println("With MAX_VALUE-1 : "+(after-before)/1e6);
}
}
}

private static void runWithMaxValue()
{
final int n = Integer.MAX_VALUE;
int i = 0;
while (i++ < n) {}
}

private static void runWithMaxValueMinusOne()
{
final int n = Integer.MAX_VALUE-1;
int i = 0;
while (i++ < n) {}
}
}

它基本上运行相同的循环,while (i++ < n){} , 其中限制 n曾经设置为 Integer.MAX_VALUE , 一次到 Integer.MAX_VALUE-1 .

当使用 JDK 1.7.0_21 和

在 Win7/64 上执行此操作时
java -server MissedLoopOptimization

计时结果如下:

...
With MAX_VALUE : 1285.227081
With MAX_VALUE : 1274.36311
With MAX_VALUE : 1282.992203
With MAX_VALUE : 1292.88246
With MAX_VALUE : 1280.788994
With MAX_VALUE-1 : 6.96E-4
With MAX_VALUE-1 : 3.48E-4
With MAX_VALUE-1 : 0.0
With MAX_VALUE-1 : 0.0
With MAX_VALUE-1 : 3.48E-4

显然,对于 MAX_VALUE-1 的情况,JIT 做了人们可以预料的事情:它检测到循环是无用的,并完全消除它。但是,当循环运行到 MAX_VALUE 时,它不会删除循环。 .

java -server -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+LogCompilation -XX:+PrintAssembly MissedLoopOptimization

日志包含以下程序集,用于运行至 MAX_VALUE 的方法:

Decoding compiled method 0x000000000254fa10:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} &apos;runWithMaxValue&apos; &apos;()V&apos; in &apos;MissedLoopOptimization&apos;
# [sp+0x20] (sp of caller)
0x000000000254fb40: sub $0x18,%rsp
0x000000000254fb47: mov %rbp,0x10(%rsp) ;*synchronization entry
; - MissedLoopOptimization::runWithMaxValue@-1 (line 29)
0x000000000254fb4c: mov $0x1,%r11d
0x000000000254fb52: jmp 0x000000000254fb63
0x000000000254fb54: nopl 0x0(%rax,%rax,1)
0x000000000254fb5c: data32 data32 xchg %ax,%ax
0x000000000254fb60: inc %r11d ; OopMap{off=35}
;*goto
; - MissedLoopOptimization::runWithMaxValue@11 (line 30)
0x000000000254fb63: test %eax,-0x241fb69(%rip) # 0x0000000000130000
;*goto
; - MissedLoopOptimization::runWithMaxValue@11 (line 30)
; {poll}
0x000000000254fb69: cmp $0x7fffffff,%r11d
0x000000000254fb70: jl 0x000000000254fb60 ;*if_icmpge
; - MissedLoopOptimization::runWithMaxValue@8 (line 30)
0x000000000254fb72: add $0x10,%rsp
0x000000000254fb76: pop %rbp
0x000000000254fb77: test %eax,-0x241fb7d(%rip) # 0x0000000000130000
; {poll_return}
0x000000000254fb7d: retq
0x000000000254fb7e: hlt
0x000000000254fb7f: hlt
[Exception Handler]
[Stub Code]
0x000000000254fb80: jmpq 0x000000000254e820 ; {no_reloc}
[Deopt Handler Code]
0x000000000254fb85: callq 0x000000000254fb8a
0x000000000254fb8a: subq $0x5,(%rsp)
0x000000000254fb8f: jmpq 0x0000000002528d00 ; {runtime_call}
0x000000000254fb94: hlt
0x000000000254fb95: hlt
0x000000000254fb96: hlt
0x000000000254fb97: hlt

0x7fffffff 相比,可以清楚地看到循环。并跳转回 inc .与此相反,它运行到 MAX_VALUE-1 的情况下的程序集:

Decoding compiled method 0x000000000254f650:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} &apos;runWithMaxValueMinusOne&apos; &apos;()V&apos; in &apos;MissedLoopOptimization&apos;
# [sp+0x20] (sp of caller)
0x000000000254f780: sub $0x18,%rsp
0x000000000254f787: mov %rbp,0x10(%rsp) ;*synchronization entry
; - MissedLoopOptimization::runWithMaxValueMinusOne@-1 (line 36)
0x000000000254f78c: add $0x10,%rsp
0x000000000254f790: pop %rbp
0x000000000254f791: test %eax,-0x241f797(%rip) # 0x0000000000130000
; {poll_return}
0x000000000254f797: retq
0x000000000254f798: hlt
0x000000000254f799: hlt
0x000000000254f79a: hlt
0x000000000254f79b: hlt
0x000000000254f79c: hlt
0x000000000254f79d: hlt
0x000000000254f79e: hlt
0x000000000254f79f: hlt
[Exception Handler]
[Stub Code]
0x000000000254f7a0: jmpq 0x000000000254e820 ; {no_reloc}
[Deopt Handler Code]
0x000000000254f7a5: callq 0x000000000254f7aa
0x000000000254f7aa: subq $0x5,(%rsp)
0x000000000254f7af: jmpq 0x0000000002528d00 ; {runtime_call}
0x000000000254f7b4: hlt
0x000000000254f7b5: hlt
0x000000000254f7b6: hlt
0x000000000254f7b7: hlt

所以我的问题是:Integer.MAX_VALUE 有什么特别之处?这会阻止 JIT 以与 Integer.MAX_VALUE-1 相同的方式对其进行优化?我的猜测是这与 cmp指令,用于 signed 算术,但仅此一项并不是一个真正令人信服的理由。谁能解释一下,甚至可以指出处理这种情况的 OpenJDK HotSpot 代码?

(旁白:我希望答案也能解释 i++++i 在另一个问题中所要求的不同行为,假设缺少优化的原因是(显然)实际上是由Integer.MAX_VALUE循环限制引起的)

最佳答案

我还没有挖掘 Java 语言规范,但我猜它与这种差异有关:

  • i++ < (Integer.MAX_VALUE - 1)永不溢出。一次i达到Integer.MAX_VALUE - 1它增加到 Integer.MAX_VALUE然后循环终止。

  • i++ < Integer.MAX_VALUE包含整数溢出。一次i达到Integer.MAX_VALUE ,它加一导致溢出,然后循环终止。

我假设 JIT 编译器“不愿意”优化具有这种角落条件的循环 - 有一个 whole bunch of bugs w.r.t.整数溢出条件下的循环优化,因此很可能不情愿。

可能还有一些硬性要求不允许优化整数溢出,尽管我有点怀疑,因为整数溢出在 Java 中不能直接检测或以其他方式处理。

关于java - JIT 未优化涉及 Integer.MAX_VALUE 的循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25326377/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com