Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
&引用;“快速”;Java中的整数幂_Java_Algorithm_Performance - Fatal编程技术网

&引用;“快速”;Java中的整数幂

&引用;“快速”;Java中的整数幂,java,algorithm,performance,Java,Algorithm,Performance,[简短回答:糟糕的基准测试方法。你可能认为我现在已经明白了这一点。] 问题是“找到一种快速计算x^y的方法,其中x和y是正整数”。典型的“快速”算法如下所示: public long fastPower(int x, int y) { // Replaced my code with the "better" version described below, // but this version isn't measurably faster than what I had befor

[简短回答:糟糕的基准测试方法。你可能认为我现在已经明白了这一点。]

问题是“找到一种快速计算x^y的方法,其中x和y是正整数”。典型的“快速”算法如下所示:

public long fastPower(int x, int y) {
  // Replaced my code with the "better" version described below,
  // but this version isn't measurably faster than what I had before
  long base = x; // otherwise, we may overflow at x *= x.
  long result = y % 2 == 1 ? x : 1;
  while (y > 1) {
    base *= base;
    y >>= 1;
    if (y % 2 == 1) result *= base;
  }

  return result;
}
public long naivePower(int x, int y) {
  long result = 1;
  for (int i = 0; i < y; i++) {
    result *= x;
  }
  return result;
}
我想看看这比调用Math.pow()或使用简单的方法(如x乘以y)快多少,比如:

public long fastPower(int x, int y) {
  // Replaced my code with the "better" version described below,
  // but this version isn't measurably faster than what I had before
  long base = x; // otherwise, we may overflow at x *= x.
  long result = y % 2 == 1 ? x : 1;
  while (y > 1) {
    base *= base;
    y >>= 1;
    if (y % 2 == 1) result *= base;
  }

  return result;
}
public long naivePower(int x, int y) {
  long result = 1;
  for (int i = 0; i < y; i++) {
    result *= x;
  }
  return result;
}
public long-power(int x,int y){
长期结果=1;
for(int i=0;i
编辑:好的,有人(正确地)向我指出,我的基准测试代码并没有使用结果,这完全把一切都抛在脑后了。一旦我开始使用结果,我仍然看到天真的方法比“快速”方法快25%左右

原文:

我非常惊讶地发现,Naiver方法比“fast”版本快4倍,而“fast”版本本身比Math.pow()版本快3倍。
我的测试使用10000000次(然后是1亿次,只是为了绝对确保JIT有时间预热),每次使用随机值(防止调用被优化)2while
循环运行
log2(y)
次,而for循环运行
y
次,因此根据您的输入,一个跑得比另一个快

while循环在最坏情况下运行:

  • 比较(
    while
    conditional)
  • 一个模
  • 一个比较,最坏情况下还有三个操作:
  • 乘法运算
  • 位移位赋值
  • 又一次乘法,最后
  • 减量
  • 而的朴素
    循环运行:

  • 比较(
    用于
    条件)
  • 乘法运算,以及
  • 增量(
  • 迭代器的
    )
    因此,对于
    y
    的较小值,您希望naive循环更快,因为
    for
    循环中较少的操作数优于“快速”方法的log2缩减,只要这些额外操作所损失的时间大于log2缩减y所获得的时间

    如果你无法达到你的基准,那么尝试细分你的结果就没有什么意义了。它们可能是由于输入选择不当、错误的基准测试实践(例如在一个测试之前运行另一个测试(从而给JVM“预热”时间)等等。请分享您的基准代码,而不仅仅是您的结果

    我建议在您的测试中包括Guava的(),这是一种大量使用且经过良好基准测试的方法。虽然您可能能够通过某些输入击败它,但在一般情况下,您不太可能改进它的运行时(如果可以,他们很乐意听到)


    毫不奇怪,
    Math.pow()
    比纯正整数算法的性能更差。看看“快速”与“幼稚”的实现,很明显,这在很大程度上取决于您选择的输入,正如Mike'Pomax'Kamermans所建议的那样。对于
    y
    的小值,“幼稚”解决方案显然需要做更少的工作。但是对于更大的值,我们通过“快速”实现节省了大量的迭代次数。

    在我看来,问题中的第一个
    fastPower(base,exponent)
    如果没有给出错误的结果,那就是错误的。(下面的
    intPower()
    的第一个版本是buggy,除了有点误导性的基准测试结果外,还给出了错误的结果。)
    由于评论“格式化功能”,另一个指数化的表现形式是通过平方来争论作为答案:

    static public long intPower(int base, int exponent) {
        if (0 == base
            || 1 == base)
            return base;
        int y = exponent;
        if (y <= 0)
            return 0 == y ? 1 : -1 != base ? 0 : y % 2 == 1 ? -1 : 1;
        long result = y % 2 == 1 ? base : 1,
            power = base;
        while (1 < y) {
            power *= power;
            y >>= 1; // easier to see termination after Type.SIZE iterations
            if (y % 2 == 1)
                result *= power;
        }
        return result;
    }
    

    您的
    快速电源有两个问题:

  • 最好将
    y%2==0
    替换为
    (y&1)==0
    ;按位运算速度更快
  • 您的代码总是递减
    y
    并执行额外的乘法,包括
    y
    为偶数的情况。最好把这部分放在
    else
    子句中
  • 无论如何,我猜你的基准测试方法并不完美。4倍的性能差异听起来很奇怪,如果没有完整的代码就无法解释

    应用上述改进后,我使用基准测试验证了
    fastPower
    确实比
    naivePower
    快,系数为1.3x到2x

    package bench;
    
    import org.openjdk.jmh.annotations.*;
    
    @State(Scope.Benchmark)
    public class FastPow {
        @Param("3")
        int x;
        @Param({"25", "28", "31", "32"})
        int y;
    
        @Benchmark
        public long fast() {
            return fastPower(x, y);
        }
    
        @Benchmark
        public long naive() {
            return naivePower(x, y);
        }
    
        public static long fastPower(long x, int y) {
            long result = 1;
            while (y > 0) {
                if ((y & 1) == 0) {
                    x *= x;
                    y >>>= 1;
                } else {
                    result *= x;
                    y--;
                }
            }
            return result;
        }
    
        public static long naivePower(long x, int y) {
            long result = 1;
            for (int i = 0; i < y; i++) {
                result *= x;
            }
            return result;
        }
    }
    
    注意:整数乘法运算速度非常快。不要期望通过适合
    long
    的值来获得巨大的性能改进。快速幂算法的优势将在指数较大的
    biginger
    上体现出来

    更新 由于作者发布了基准测试,我必须承认,令人惊讶的性能结果来自常见的基准测试陷阱。 我在保留原始方法的同时改进了基准测试,现在它表明
    FastPower
    确实比
    NaivePower

    改进版本中的关键更改是什么

  • 应该在不同的JVM实例中分别测试不同的算法,以防止配置文件污染
  • 必须多次调用基准以允许正确编译/重新编译,直到达到稳定状态
  • 一个基准测试应该放在一个单独的方法中,以避免堆栈替换问题
  • y%2
    被替换为
    y&1
    ,因为HotSpot不会自动执行此优化
  • 最小化主基准循环中不相关操作的影响

  • 手工编写微基准是一项困难的任务。这就是为什么强烈建议使用适当的基准测试框架,如。

    确定
    if(odd)
    控制哪些操作?快速求幂算法对于机器整数算法没有多大价值。只有当一次乘法的成本明显大于任何一次乘法的减量时
    package bench;
    
    import org.openjdk.jmh.annotations.*;
    
    @State(Scope.Benchmark)
    public class FastPow {
        @Param("3")
        int x;
        @Param({"25", "28", "31", "32"})
        int y;
    
        @Benchmark
        public long fast() {
            return fastPower(x, y);
        }
    
        @Benchmark
        public long naive() {
            return naivePower(x, y);
        }
    
        public static long fastPower(long x, int y) {
            long result = 1;
            while (y > 0) {
                if ((y & 1) == 0) {
                    x *= x;
                    y >>>= 1;
                } else {
                    result *= x;
                    y--;
                }
            }
            return result;
        }
    
        public static long naivePower(long x, int y) {
            long result = 1;
            for (int i = 0; i < y; i++) {
                result *= x;
            }
            return result;
        }
    }
    
    Benchmark      (x)  (y)   Mode  Cnt    Score   Error   Units
    FastPow.fast     3   25  thrpt   10  103,406 ± 0,664  ops/us
    FastPow.fast     3   28  thrpt   10  103,520 ± 0,351  ops/us
    FastPow.fast     3   31  thrpt   10   85,390 ± 0,286  ops/us
    FastPow.fast     3   32  thrpt   10  115,868 ± 0,294  ops/us
    FastPow.naive    3   25  thrpt   10   76,331 ± 0,660  ops/us
    FastPow.naive    3   28  thrpt   10   69,527 ± 0,464  ops/us
    FastPow.naive    3   31  thrpt   10   54,407 ± 0,231  ops/us
    FastPow.naive    3   32  thrpt   10   56,127 ± 0,207  ops/us