JIT没有优化涉及Integer.MAX_VALUE的循环 [英] JIT not optimizing loop that involves Integer.MAX_VALUE

查看:94
本文介绍了JIT没有优化涉及Integer.MAX_VALUE的循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在为另一个问题撰写答案时,我注意到JIT优化的一个奇怪的边界情况。

While writing an answer to another question, I noticed a strange border case for JIT optimization.

以下程序不是Microbenchmark而旨在可靠地测量执行时间(如指出的那样)在另一个问题的答案中)。它仅用作 MCVE 来重现该问题:

The following program is not a "Microbenchmark" and not intended to reliably measure an execution time (as pointed out in the answers to the other question). It is solely intended as an MCVE to reproduce the issue:

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

It basically runs the same loop, while (i++ < n){}, where the limit n is once set to Integer.MAX_VALUE, and once to Integer.MAX_VALUE-1.

在Win7 / 64上使用JDK 1.7.0_21和

When executing this on Win7/64 with JDK 1.7.0_21 and

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 运行时,会删除循环。

Obviously, for the case of MAX_VALUE-1, the JIT does what one could expect: It detects that the loop is useless, and completely eliminates it. However, it does not remove the loop when it is running up to MAX_VALUE.

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

该日志包含以下程序集,该程序集最多可运行 MAX_VALUE

The log contains the following assembly for the method that runs up to 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

One can clearly see the loop, with the comparison to 0x7fffffff and the jump back to inc. In contrast to that, the assembly for the case where it is running up to 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 指令有关,该指令适用于签名算术,但仅此一点并不是一个令人信服的理由。任何人都可以解释一下这个问题吗,甚至可能会给出一个指向这个案例的OpenJDK HotSpot代码的指针吗?

So my question is: What is so special about the Integer.MAX_VALUE that prevents the JIT from optimizing it in the same way as it does for Integer.MAX_VALUE-1? My guess would be that has to do with the cmp instruction, which is intended for signed arithmetic, but that alone is not really a convincing reason. Can anybody explain this, and maybe even give a pointer to the OpenJDK HotSpot code where this case is treated?

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

(An aside: I hope that the answer will also explain the different behavior between i++ and ++i that was asked for in the other question, assuming that the reason for the missing optimization is (obviously) actually caused by the Integer.MAX_VALUE loop limit)

推荐答案

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

I have not dug up the Java Language Specification, but I'd guess that it has to do with this difference:


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

  • i++ < (Integer.MAX_VALUE - 1) never overflows. Once i reaches Integer.MAX_VALUE - 1 it is incremented to Integer.MAX_VALUE and then the loop terminates.

i ++< Integer.MAX_VALUE 包含整数溢出。一旦 i 到达 Integer.MAX_VALUE ,它就会增加1,导致溢出,然后循环终止。

i++ < Integer.MAX_VALUE contains an integer overflow. Once i reaches Integer.MAX_VALUE, it is incremented by one causing an overflow and then the loop terminates.

我认为JIT编译器不情愿优化输出具有这种拐角条件的循环 - 有一个一大堆错误 wrt整数溢出条件下的循环优化,因此可能非常需要磁阻。

I assume that the JIT compiler is "reluctant" to optimize-out loops with such corner conditions - there was a whole bunch of bugs w.r.t. loop optimization in integer overflow conditions, so that reluctance is probably quite warranted.

可能还有一些硬性要求不允许对整数溢出进行优化,尽管我以某种方式怀疑,因为整数溢出不能直接检测到或以Java方式处理。

There may also be some hard requirement that does not allow integer overflows to be optimized-out, although I somehow doubt that since integer overflows are not directly detectable or otherwise handled in Java.

这篇关于JIT没有优化涉及Integer.MAX_VALUE的循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆