【问题标题】:java method: java.lang.Integer.numberOfLeadingZeros(int) can be optimizedjava方法:java.lang.Integer.numberOfLeadingZeros(int) 可以优化
【发布时间】:2017-10-12 09:25:02
【问题描述】:

源代码是:

public static int numberOfLeadingZeros(int i) {
    // HD, Figure 5-6
    if (i == 0)
        return 32;
    int n = 1;
    if (i >>> 16 == 0) { n += 16; i <<= 16; }
    if (i >>> 24 == 0) { n +=  8; i <<=  8; }
    if (i >>> 28 == 0) { n +=  4; i <<=  4; }
    if (i >>> 30 == 0) { n +=  2; i <<=  2; }
    n -= i >>> 31;
    return n;
}

我认为可以优化,应该添加以下条件:

if (i < 0)
        return 0;

完全优化的代码是:

public static int numberOfLeadingZeros(int i) {
    if(i<=0) {
        return i < 0 ? 0 : 32;
    }
    int n = 1;
    if (i >>> 16 == 0) { n += 16; i <<= 16; }
    if (i >>> 24 == 0) { n +=  8; i <<=  8; }
    if (i >>> 28 == 0) { n +=  4; i <<=  4; }
    if (i >>> 30 == 0) { n +=  2; i <<=  2; }
    n -= i >>> 31;
    return n;
}

【问题讨论】:

  • 你的问题是?
  • 我的问题是:优化后的代码是否正确?或者,我的想法对吗?
  • 因为我不知道你想要达到什么目的......不知道
  • 如果它只是一个“优化”,写一些单元测试来证明你的实现的正确性!
  • 我觉得优化后的代码效率更高,是吗?

标签: java jvm openjdk jvm-hotspot


【解决方案1】:

理论上是的,你的建议是有道理的。

实际上,除非你使用一个外来的JVM,否则不会有任何区别,因为the method is intrinsic,所以执行的代码不是你在Java类中可以找到的代码。

例如在 x86/64 cpus 上,代码为 here 并使用 bsrl CPU 指令,这是您希望的最快速度。

【讨论】:

  • 所以JDK中的内部方法源只是为了阅读,它永远不会被执行?
  • @Jason 这取决于运行程序的 JVM - 如果该 JVM 没有该方法的内在代码,它将运行 java 代码。
  • 那么,我的建议对 JVM 还是有用的,对吧?
  • @Jason 解释过,JVM会执行一些cpu指令,而不是java代码,所以不需要java代码优化。
  • 所以这意味着在Open-JDK或Oracle热点中,标记为intrinsic的方法永远不会运行java代码。对?那为什么不直接删除java代码,只像调用native方法一样运行呢?
【解决方案2】:

除了这个方法可能会被热点的内在操作所取代这一事实之外,如果数字为负数,这种对负数的检查只是一种改进。对于正数,它只是要评估的附加条件

因此,此优化的价值取决于此函数中否定参数的可能性。当我考虑这个函数的典型用例时,我会认为负值是一种极端情况,而不是典型的论点。

请注意,开始时对零的特殊处理不是优化,而是一种要求,因为如果没有这种特殊处理,算法将不会返回正确的零结果。


由于您的错误报告产生于寻找替代方案(也显示在您更新的问题中),它改进了负数情况而不影响正数情况的性能,因为它将所需的零测试和负数测试融合到一个单一的预测试,没有什么能阻止建议的优化。

【讨论】:

  • 值得做优化的原因有四个,1、逻辑上:如果参数为负,下面的二叉搜索就完全没有必要了,这样可以减少移位操作,节省CPU力量。 2.在实践中,有很多对性能敏感的微型CPU设备,优化对他们来说是有意义的。 3.在某些情况下,否定参数的可能性很大,例如接受来自外部的随机数。4.代码显然是从高清书中复制的,没有很好地考虑(优化)有符号的负数。
  • “接受来自外部的随机数”并将它们传递给numberOfLeadingZeros 是一个牵强附会的场景,没有实际意义。我检查了所有 JDK 内部使用以及其他一些应用程序,没有一个包含负数的可能性。
  • @Eugene:嗯,这对 IDE 来说并不难,而且它们在预期的用例集中,这确实有助于分析它们。
【解决方案3】:

已在 oracle 错误数据库上创建错误:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8189230

【讨论】:

  • @Holger,@Eugene,@assylias,根据上面链接的错误评论,Java 官方开发团队似乎也同意我的建议。他们还根据它提出了进一步的优化建议。
  • 好吧,通过建议的更改(改用if(i &lt;= 0) return i == 0? 32: 0;),为普通(正数)情况评估的条件数与旧代码中的相同,所以现在它确实是可以在不损害正数情况的情况下改善负数情况的更改,因此值得考虑更改(请记住那里的评论:“当然应该证明更好的性能”)。
  • @Holger 实际上,java官方开发者给出的建议的改变并不是最好的,更好的代码应该是'if(i
  • 为什么i &lt; 0? 0 : 32i == 0? 32: 0 更好?
  • 请阅读链接的问答。您的测试犯了几个 那里列出的基本错误。使用支持否定情况的实现是否对现实生活中的应用有意义,这是一个完全不同的问题。如果我是该方法的维护者,我会使用像 if(i&lt;=0) return ((i&gt;&gt;&gt;31)^1)&lt;&lt;5; 这样的无分支解决方案,它不支持一种情况而不是另一种情况,并不是说只影响解释执行的优化有那么重要。但无论如何,值得了解未来的链接问答......
猜你喜欢
  • 1970-01-01
  • 2013-05-31
  • 2010-09-29
  • 1970-01-01
  • 2012-08-23
  • 1970-01-01
  • 2016-07-01
  • 2018-05-22
  • 1970-01-01
相关资源
最近更新 更多