当前位置: 首页 > 面试题库 >

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

孟子墨
2023-03-14
问题内容

在写另一个问题的答案时,我注意到用于JIT优化的奇怪边框。

以下程序 不是 “ Microbenchmark”, 也不
旨在可靠地衡量执行时间(如对另一个问题的回答所指出)。它仅用作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

当在Win7 / 64上使用JDK 1.7.0_21和

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-1JIT 的情况,JIT可以完成预期的工作:它检测到循环是无用的,并完全消除了循环。但是,它并 没有
在运行状态下达到消除环路MAX_VALUE

通过从以下位置开始查看JIT程序集输出可以确认此观察结果:

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指令有关的,该指令是用于有
符号 算术的,但仅此一项并不是真正令人信服的原因。谁能解释这个问题,甚至可以提供一个指向处理这种情况的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编译器“不愿意”在这种极端情况下优化循环-
在整数溢出条件下有很多bug会导致
wrt循环优化,因此可能确实需要这样做。

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



 类似资料:
  • 问题内容: 在有关Hotspot中的JIT的讲座中,我想提供尽可能多的JIT执行的特定优化示例。 我只知道“方法内联”,但是应该更多。为每个示例投票。 问题答案: 好吧,您应该阅读Brian Goetz的文章以获取示例。 简而言之,HotSpot可以并且将: 内联方法 连接同一对象上的相邻块 如果无法从其他线程访问监视器,则消除锁定 消除无效代码(因此大多数微基准都是毫无意义的) 对非变量的直接存

  • 问题内容: 在分析这里最近一个问题的结果时,我遇到了一个非常奇怪的现象:显然HotSpot的JIT优化的额外一层实际上减慢了我的计算机的执行速度。 这是我用于测量的代码: 该代码非常微妙,所以让我指出一些重要的方面: “普通索引”变体使用直接变量作为数组索引。HotSpot可以轻松确定整个循环的范围并消除数组边界检查; by的“ masked index”变体索引实际上等于,但是通过AND-mas

  • 在处理高度并发的单例类的单元测试时,我偶然发现了以下奇怪的行为(在JDK 1.8.0\U 162上测试): main()方法的最后两行在INSTANCE的值上不一致-我猜JIT完全摆脱了该方法,因为该字段是静态final。删除final关键字可以使代码输出正确的值。 撇开你对单例的同情(或缺乏同情)不谈,暂时忘记像这样使用反射是在自找麻烦——我的假设是正确的吗?JIT优化是罪魁祸首?如果是这样的话

  • 好心协助。我正在准备Java7Programmer1考试,在一次enthuware测试中遇到了这个问题。 考虑以下方法: 和下面的代码片段: 它会印什么? 结尾问题 根据运算符优先级的规则,我首先计算++s,得到s的值6,然后在mx方法中使用6得到值8。接下来我加了6+8+6=20。最后以s=6+20=26进行赋值操作。

  • 智能机器人可以做到的事情可以很复杂:文字、语音、视频识别与合成;自然语言理解、人机对话;以及驱动硬件设备形成的“机器”人。作为一个只有技术和时间而没有金钱的IT人士,我仅做自然语言和人工智能相关的内容,不涉及硬件,也不涉及不擅长的多媒体识别和合成。所以索性就做一个可以和你说话,帮你解决问题的聊天机器人吧。 聊天机器人涉及到的知识主要是自然语言处理,当然这包括了:语言分析和理解、语言生成、机器学习、

  • 问题内容: 我使用java for循环进行了一些运行时测试,并发现了一种奇怪的行为。对于我的代码,我需要原始类型(例如int,double等)的包装对象来模拟io和输出参数,但这不是重点。只是看我的代码。具有字段访问权限的对象如何比原始类型更快? 优先类型的循环: 结果: MicroTime原语(最大值:= 10000.0):110 MicroTime原语(最大值:= 100000.0):1081