加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java优化器是否会记住计算值?

发布时间:2020-12-15 05:03:22 所属栏目:Java 来源:网络整理
导读:哪个 fizzbuzz实现效率更高? public static void fizzBuzz1(int n) { boolean fizzed,buzzed; for(int i = 1; i = n; i++) { fizzed = buzzed = false; if(i % 3 == 0) fizzed = true; if(i % 5 == 0) buzzed = true; if(fizzed buzzed) System.out.println
哪个 fizzbuzz实现效率更高?

public static void fizzBuzz1(int n)
    {
        boolean fizzed,buzzed;
        for(int i = 1; i <= n; i++)
        {
            fizzed = buzzed = false;
            if(i % 3 == 0)
                fizzed = true;
            if(i % 5 == 0)
                buzzed = true;
            if(fizzed  && buzzed)
                System.out.println("FizzBuzz");
            else if(fizzed)
                System.out.println("Fizz");
            else if(buzzed)
                System.out.println("Buzz");
            else
                System.out.println(i);
        }
    }

    public static void fizzBuzz2(int n)
    {
        for(int i = 1; i <= n; i++)
        {
            if(i % 3 == 0  && i % 5 == 0)
                System.out.println("FizzBuzz");
            else if(i % 3 == 0)
                System.out.println("Fizz");
            else if(i % 5 == 0)
                System.out.println("Buzz");
            else
                System.out.println(i);
        }
    }

解决方法

这一次采取了一些有趣的转折.

首先,我试着看一下使用原始方法生成的程序集.但是,JIT进行了一些内联和优化,这包括System.out.println调用,因此生成的程序集输出太大(对我而言)在合理的时间内对其进行合理分析.

所以我简化了整个过程,以便能够专注于实际的问题.最后,我运行了以下程序:

class Test04
{
    public static void main(String args[])
    {
        long sum = 0;
        for (int i=1000; i<12000; i++)
        {
            sum += fizzBuzz1(i);
            sum += fizzBuzz2(i);
        }
        System.out.println(sum);
    }

    public static long fizzBuzz1(int n)
    {
        long sum = 0;
        for(int i = 1; i <= n; i++)
        {
            sum += fizzBuzzCore1(i);
        }
        return sum;
    }

    public static long fizzBuzzCore1(int i)
    {
        boolean fizzed = false;
        boolean buzzed = false;
        if(i % 3 == 0)
            fizzed = true;
        if(i % 5 == 0)
            buzzed = true;
        if(fizzed  && buzzed)
            return 4;
        else if(fizzed)
            return 3;
        else if(buzzed)
            return 2;
        else
            return 1;
    }


    public static long fizzBuzz2(int n)
    {
        long sum = 0;
        for(int i = 1; i <= n; i++)
        {
            sum += fizzBuzzCore2(i);
        }
        return sum;
    }

    public static long fizzBuzzCore2(int i)
    {
        if(i % 3 == 0  && i % 5 == 0)
            return 4;
        else if(i % 3 == 0)
            return 3;
        else if(i % 5 == 0)
            return 2;
        else
            return 1;
    }

}

返回值旨在防止他完全优化掉调用,并提取旨在保持必须比较的组件输出的大小尽可能小的“核心”方法.

(注意:当然,这些修改可能会影响优化.例如,JIT有一个方法可能具有的字节码指令数量的限制,在它被认为太大而无法内联之前,-XX:MaxInlineSize = 35但是两种方法的效果大致相同,因此仍然可以得出关于实际问题的所需信息.

而且,并不是一个惊喜:在最后一次优化之后,两种方法的汇编代码都包含相同的指令 – 这里是fizzBu??zzCore1的汇编作为参考:

Decoding compiled method 0x00000000026c0090:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} {0x0000000057260528} &apos;fizzBuzzCore1&apos; &apos;(I)J&apos; in &apos;Test04&apos;
  # parm0:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x00000000026c01c0: sub    $0x18,%rsp
  0x00000000026c01c7: mov    %rbp,0x10(%rsp)    ;*synchronization entry
                                                ; - Test04::fizzBuzzCore1@-1 (line 27)

  0x00000000026c01cc: movslq %edx,%r10
  0x00000000026c01cf: mov    %edx,%r11d
  0x00000000026c01d2: sar    $0x1f,%r11d        ;*irem
                                                ; - Test04::fizzBuzzCore1@6 (line 29)

  0x00000000026c01d6: imul   $0x66666667,%r10,%r8
  0x00000000026c01dd: imul   $0x55555556,%r10
  0x00000000026c01e4: sar    $0x21,%r8
  0x00000000026c01e8: sar    $0x20,%r10
  0x00000000026c01ec: mov    %r8d,%r8d
  0x00000000026c01ef: sub    %r11d,%r8d         ;*irem
                                                ; - Test04::fizzBuzzCore1@14 (line 31)

  0x00000000026c01f2: mov    %r10d,%r10d
  0x00000000026c01f5: sub    %r11d,%r10d        ;*irem
                                                ; - Test04::fizzBuzzCore1@6 (line 29)

  0x00000000026c01f8: mov    %r8d,%r11d
  0x00000000026c01fb: shl    $0x2,%r11d
  0x00000000026c01ff: add    %r8d,%r11d         ;*irem
                                                ; - Test04::fizzBuzzCore1@14 (line 31)

  0x00000000026c0202: mov    %r10d,%r9d
  0x00000000026c0205: shl    %r9d
  0x00000000026c0208: add    %r10d,%r9d         ;*irem
                                                ; - Test04::fizzBuzzCore1@6 (line 29)

  0x00000000026c020b: cmp    %r9d,%edx
  0x00000000026c020e: jne    0x00000000026c021c  ;*ifeq
                                                ; - Test04::fizzBuzzCore1@21 (line 33)

  0x00000000026c0210: cmp    %r11d,%edx
  0x00000000026c0213: jne    0x00000000026c021c  ;*ifeq
                                                ; - Test04::fizzBuzzCore1@25 (line 33)

  0x00000000026c0215: mov    $0x4,%eax
  0x00000000026c021a: jmp    0x00000000026c0239  ;*iload_1
                                                ; - Test04::fizzBuzzCore1@32 (line 35)

  0x00000000026c021c: cmp    %r9d,%edx
  0x00000000026c021f: jne    0x00000000026c0228  ;*ifeq
                                                ; - Test04::fizzBuzzCore1@33 (line 35)

  0x00000000026c0221: mov    $0x3,%eax
  0x00000000026c0226: jmp    0x00000000026c0239
  0x00000000026c0228: cmp    %r11d,%edx
  0x00000000026c022b: jne    0x00000000026c0234  ;*ifeq
                                                ; - Test04::fizzBuzzCore1@41 (line 37)

  0x00000000026c022d: mov    $0x2,%eax
  0x00000000026c0232: jmp    0x00000000026c0239
  0x00000000026c0234: mov    $0x1,%eax          ;*irem
                                                ; - Test04::fizzBuzzCore1@6 (line 29)

  0x00000000026c0239: add    $0x10,%rsp
  0x00000000026c023d: pop    %rbp
  0x00000000026c023e: test   %eax,-0x2470244(%rip)        # 0x0000000000250000
                                                ;   {poll_return}
  0x00000000026c0244: retq   
  0x00000000026c0245: hlt    
  0x00000000026c0246: hlt    
  0x00000000026c0247: hlt    
  0x00000000026c0248: hlt    
  0x00000000026c0249: hlt    
  0x00000000026c024a: hlt    
  0x00000000026c024b: hlt    
  0x00000000026c024c: hlt    
  0x00000000026c024d: hlt    
  0x00000000026c024e: hlt    
  0x00000000026c024f: hlt    
  0x00000000026c0250: hlt    
  0x00000000026c0251: hlt    
  0x00000000026c0252: hlt    
  0x00000000026c0253: hlt    
  0x00000000026c0254: hlt    
  0x00000000026c0255: hlt    
  0x00000000026c0256: hlt    
  0x00000000026c0257: hlt    
  0x00000000026c0258: hlt    
  0x00000000026c0259: hlt    
  0x00000000026c025a: hlt    
  0x00000000026c025b: hlt    
  0x00000000026c025c: hlt    
  0x00000000026c025d: hlt    
  0x00000000026c025e: hlt    
  0x00000000026c025f: hlt    
[Exception Handler]
[Stub Code]
  0x00000000026c0260: jmpq   0x000000000261c560  ;   {no_reloc}
[Deopt Handler Code]
  0x00000000026c0265: callq  0x00000000026c026a
  0x00000000026c026a: subq   $0x5,(%rsp)
  0x00000000026c026f: jmpq   0x00000000025f6f40  ;   {runtime_call}
  0x00000000026c0274: hlt    
  0x00000000026c0275: hlt    
  0x00000000026c0276: hlt    
  0x00000000026c0277: hlt

但…

然而,令人惊讶的是:它根本不计算模运算!

至少,没有明确说明:此代码中没有出现idiv指令!所以JIT真的努力避免代价高昂的分歧,通过做一些令人讨厌的,讨厌的苦涩的黑客攻击:指示

0x00000000026c01d6: imul   $0x66666667,%r10
  (and following...)

是一个部门的“无分工”实施.例如,该方法

private static int divideBy3(int n)
{
    long r10 = n;
    r10 *= 0x55555556L;
    r10 >>>= 0x20;
    long r10d = r10 & 0xFFFFFFFFL;
    return (int)r10d;
}

使用这些魔术常数并移位以计算除以3(类似地,对于5除以另一个常数).我自己没有做过数学计算,但可以在Page 32 of the “INTEGER DIVISION BY CONSTANTS” document from Hacker’s Delight找到关于如何从中得到模运算的解释.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读