【问题标题】:Will the java compiler optimise calls to Math.exp with a constant?java 编译器会用常量优化对 Math.exp 的调用吗?
【发布时间】:2018-02-12 14:44:44
【问题描述】:

如果我正在实现一个包含 1-Math.exp(-50) 的公式并希望结果具有高性能,我应该预先计算该常量并将其保存在 static final double 中,或者 Java 编译器是否足够聪明,可以意识到结果是常量并预先计算它,就像使用1-50/7 这样更简单的东西一样?

【问题讨论】:

  • 其实这些函数的预计算表是应用中常见的优化,需要大量使用它们。就像在密码学中,logexp 表用于有效地计算大功率。
  • 1-Math.exp(-50) 只是1.0,无需计算
  • @harold 从技术上讲它是 1-1.9287498e-22 - 所以不是 1.0 ;)
  • @TimB 技术上 1-1.9287498e-22 = 1.0
  • @harold 很有趣,1.9e-22 适合双倍,但是当你从 1 中减去它时,就不再适合了。我想这是有道理的,但它并不能回答更一般的情况。

标签: java performance compiler-optimization


【解决方案1】:

Java 编译器是否足够聪明,能够意识到结果是一个常量

根据constant expressions 上的 Java 语言规范章节,没有。表达式1-Math.exp(-50) 不会在编译时计算。

如果您有合理的理由相信它会导致性能损失,请预先计算它。 (我怀疑您是否将结果存储在 static 字段中一次并重用它。)


规则:

常量表达式是表示原始值的表达式 类型或不突然完成的字符串,并使用 只有以下:

  • 原始类型的文字和字符串类型的文字(第 3.10.1 节, §3.10.2、§3.10.3、§3.10.4、§3.10.5)

  • 转换为基本类型和转换为字符串类型(第 15.16 节)

  • 一元运算符 +、-、~ 和 ! (但不是 ++ 或 --)(§15.15.3, §15.15.4、§15.15.5、§15.15.6)

  • 乘法运算符 *、/ 和 %(第 15.17 节)

  • 加法运算符 + 和 - (§15.18)

  • 移位运算符 > 和 >>>(第 15.19 节)

  • 关系运算符 和 >=(但不是 instanceof) (§15.20)

  • 等式运算符 == 和 !=(第 15.21 节)

  • 按位和逻辑运算符 &、^ 和 | (§15.22)

  • 条件与运算符 && 和条件或运算符 || (§15.23, §15.24)

  • 三元条件运算符? : (§15.25)

  • 带括号的表达式(第 15.8.5 节),其包含的表达式是 常量表达式。

  • 引用常量变量 (§4.12.4) 的简单名称 (§6.5.6.1)。

  • TypeName 形式的限定名称 (§6.5.6.2)。标识符 参考常量变量(第 4.12.4 节)。

【讨论】:

    【解决方案2】:

    javac编译器不会预先计算值(去掉不相关的代码):

    $ javap -v Main.class
    
      public static void main(java.lang.String[]) throws java.lang.Exception;
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=4, locals=3, args_size=1
             0: dconst_1
             1: ldc2_w        #2                  // double -50.0d
             4: invokestatic  #4                  // Method java/lang/Math.exp:(D)D
             7: dsub
             8: dstore_1
             9: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
            12: dload_1
            13: invokevirtual #6                  // Method java/io/PrintStream.println:(D)V
            16: return
    }
    SourceFile: "Main.java"
    

    原文出处:

    public final class Main {
        public static void main(String[] args) throws Exception {
            final double v = 1 - Math.exp(-50);
            System.out.println(v);
        }
    }
    

    【讨论】:

    • 方法是如何实现的?也许 CPU 有预先计算好的表。
    • 这似乎很确定,对 Math.exp 的调用在生成的字节码中。谢谢:)
    • 当然,JIT 有可能(不确定)会对其进行优化 :) 毕竟 javac 非常愚蠢,所有优化都是在运行时完成的。
    • @SvetlinZarev 确实如此,这是我希望有人能揭开的秘密调味料,而我只是没有遇到过 - 它真的类似于逃避分析。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-25
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 2017-05-04
    • 2013-01-08
    • 1970-01-01
    相关资源
    最近更新 更多