这个比较Java8U40和8u31乘以BigInteger值的基准测试是否正确?

这个比较Java8U40和8u31乘以BigInteger值的基准测试是否正确?,java,performance,biginteger,jmh,Java,Performance,Biginteger,Jmh,我看到8u40更新修复了这个问题,但我没想到它会在2-3倍的时间内加速大整数乘法 以下是在两次JVM更新中通过不同公式计算阶乘的基准测试结果: 8u31 [info] Benchmark (n) Mode Cnt Score Error Units [info] JavaFactorial.recursion 1000 thrpt 5 13.994 ± 0

我看到8u40更新修复了这个问题,但我没想到它会在2-3倍的时间内加速大整数乘法

以下是在两次JVM更新中通过不同公式计算阶乘的基准测试结果:

8u31

[info] Benchmark                             (n)   Mode  Cnt       Score       Error   Units
[info] JavaFactorial.recursion              1000  thrpt    5      13.994 ±     0.175  ops/ms
[info] JavaFactorial.recursion             10000  thrpt    5       0.202 ±     0.054  ops/ms
[info] JavaFactorial.recursionPar           1000  thrpt    5      12.066 ±     8.011  ops/ms
[info] JavaFactorial.recursionPar          10000  thrpt    5       0.253 ±     0.055  ops/ms
[info] JavaFactorial.split                  1000  thrpt    5      18.255 ±     2.656  ops/ms
[info] JavaFactorial.split                 10000  thrpt    5       0.286 ±     0.063  ops/ms
8u40

[info] Benchmark                             (n)   Mode  Cnt       Score       Error   Units
[info] JavaFactorial.recursion              1000  thrpt    5      33.704 ±     0.445  ops/ms
[info] JavaFactorial.recursion             10000  thrpt    5       0.428 ±     0.199  ops/ms
[info] JavaFactorial.recursionPar           1000  thrpt    5      38.170 ±     0.433  ops/ms
[info] JavaFactorial.recursionPar          10000  thrpt    5       0.557 ±     0.030  ops/ms
[info] JavaFactorial.split                  1000  thrpt    5      46.447 ±    11.582  ops/ms
[info] JavaFactorial.split                 10000  thrpt    5       0.586 ±     0.154  ops/ms
我是否应该期待JDK的改进会导致运行更快,或者这是另一个随意的基准测试

编辑

如何验证我的发现,以证明这种加速是由于C2固有的

测试功能代码:

@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
public class JavaFactorial {
    @Param({"10", "100", "1000", "10000"})
    public int n;

    private static ForkJoinPool pool = new ForkJoinPool();

    @Benchmark
    public BigInteger loop() {
        return n > 20 ? loop(1, n) : BigInteger.valueOf(fastLoop(1, n));
    }

    @Benchmark
    public BigInteger recursion() {
        return n > 20 ? recursion(1, n) : BigInteger.valueOf(fastLoop(1, n));
    }

    @Benchmark
    public BigInteger recursionPar() {
        return n > 20 ? recursePar(1, n) : BigInteger.valueOf(fastLoop(1, n));
    }

    @Benchmark
    public BigInteger split() {
        return n > 180 ? split(n) : (n > 20 ? recursion(1, n) : BigInteger.valueOf(fastLoop(1, n)));
    }

    private long fastLoop(final int n1, int n2) {
        long p = n1;
        while (n2 > n1) {
            p = p * n2;
            n2--;
        }
        return p;
    }

    private BigInteger loop(int n1, final int n2) {
        final long l = Long.MAX_VALUE >> (32 - Integer.numberOfLeadingZeros(n2));
        long p = 1;
        BigInteger r = BigInteger.ONE;
        while (n1 <= n2) {
            if (p <= l) {
                p *= n1;
            } else {
                r = r.multiply(BigInteger.valueOf(p));
                p = n1;
            }
            n1++;
        }
        return r.multiply(BigInteger.valueOf(p));
    }

    private BigInteger recursion(final int n1, final int n2) {
        if (n2 - n1 < 65) {
            return loop(n1, n2);
        }
        final int nm = (n1 + n2) >> 1;
        return recursion(nm + 1, n2).multiply(recursion(n1, nm));
    }

    private BigInteger recursePar(final int n1, final int n2) {
        if (n2 - n1 < 700) {
            return recursion(n1, n2);
        }
        final int nm = (n1 + n2) >> 1;
        RecursiveTask<BigInteger> t = new RecursiveTask<BigInteger>() {
            protected BigInteger compute() {
                return recursePar(nm + 1, n2);
            }
        };
        if (ForkJoinTask.getPool() == pool) {
            t.fork();
        } else {
            pool.execute(t);
        }
        return recursePar(n1, nm).multiply(t.join());
    }

    private BigInteger loop2(int n1, final int n2) {
        final long l = Long.MAX_VALUE >> (32 - Integer.numberOfLeadingZeros(n2));
        long p = 1;
        BigInteger r = BigInteger.ONE;
        while (n1 <= n2) {
            if (p <= l) {
                p *= n1;
            } else {
                r = r.multiply(BigInteger.valueOf(p));
                p = n1;
            }
            n1 += 2;
        }
        return r.multiply(BigInteger.valueOf(p));
    }

    private BigInteger recursion2(final int n1, final int n2) {
        if (n2 - n1 < 65) {
            return loop2(n1, n2);
        }
        final int nm = ((n1 + n2) >> 1) | 1;
        return recursion2(nm, n2).multiply(recursion2(n1, nm - 2));
    }

    private BigInteger split(int n) {
        int i = 31 - Integer.numberOfLeadingZeros(n), s = -n, o = 1;
        BigInteger p = BigInteger.ONE, r = BigInteger.ONE;
        while (i >= 0) {
            int h = n >> i;
            int o1 = (h - 1) | 1;
            if (o < o1) {
                p = p.multiply(recursion2(o + 2, o1));
                r = r.multiply(p);
            }
            o = o1;
            s += h;
            i--;
        }
        return r.shiftLeft(s);
    }
}
@State(Scope.Benchmark)
@预热(迭代次数=3,时间=1,时间单位=时间单位。秒)
@测量(迭代次数=5,时间=1,时间单位=时间单位。秒)
@OutputTimeUnit(时间单位毫秒)
@叉子(1)
公共类阶乘{
@参数({“10”、“100”、“1000”、“10000”})
公共int n;
私有静态ForkJoinPool池=新的ForkJoinPool();
@基准
公共BigInteger循环(){
返回n>20?循环(1,n):BigInteger.valueOf(fastLoop(1,n));
}
@基准
公共BigInteger递归(){
返回n>20?递归(1,n):BigInteger.valueOf(fastLoop(1,n));
}
@基准
公共BigInteger递归par(){
返回n>20?recursePar(1,n):BigInteger.valueOf(fastLoop(1,n));
}
@基准
公共BigInteger拆分(){
返回n>180次分割(n):(n>20次递归(1,n):biginger.valueOf(fastLoop(1,n));
}
专用长快速循环(最终整数n1,整数n2){
长p=n1;
而(n2>n1){
p=p*n2;
n2-;
}
返回p;
}
私有BigInteger循环(int n1,final int n2){
最终长l=long.MAX_值>>(32-整数.前导零数(n2));
长p=1;
BigInteger r=BigInteger.1;
而n11;
返回递归(nm+1,n2)。乘法(递归(n1,nm));
}
私有BigInteger递归PAR(最终整数n1,最终整数n2){
如果(n2-n1<700){
返回递归(n1,n2);
}
最终整数nm=(n1+n2)>>1;
RecursiveTask t=新的RecursiveTask(){
受保护的BigInteger计算(){
返回递归PAR(nm+1,n2);
}
};
if(ForkJoinTask.getPool()==pool){
t、 fork();
}否则{
pool.execute(t);
}
返回recursePar(n1,nm).multiply(t.join());
}
私有BigInteger循环2(整数n1,最终整数n2){
最终长l=long.MAX_值>>(32-整数.前导零数(n2));
长p=1;
BigInteger r=BigInteger.1;
而(n11)|1;
返回递归2(nm,n2)。乘法(递归2(n1,nm-2));
}
私有BigInteger拆分(int n){
int i=31-整数。numberOfLeadingZero(n),s=-n,o=1;
BigInteger p=BigInteger.1,r=BigInteger.1;
而(i>=0){
inth=n>>i;
into1=(h-1)| 1;
if(o
这个基准肯定表明,任何大量使用
biginger
乘法的代码都会受益


您的问题的真正答案是为您自己的代码编写一个基准测试,并在
8u31
8u40
下运行。您希望您的代码更快,所以请对您的代码进行基准测试并对其进行测试。如果JRE代码或其他人的代码更快,这表明您的代码可能会受益,但您必须尝试并查看。

通过对可用JVM选项列表的调查,我发现添加到8u40中的一个选项可以准确地打开/关闭所需的内在功能

以下是8u40的
-XX:-使用multiplyTolentinic
的结果,与8u31的结果非常接近:

    [info] JavaFactorial.recursion              1000  thrpt    5      12.521 ±      3.352  ops/ms
    [info] JavaFactorial.recursion             10000  thrpt    5       0.217 ±      0.069  ops/ms
    [info] JavaFactorial.recursionPar           1000  thrpt    5      14.268 ±      8.319  ops/ms
    [info] JavaFactorial.recursionPar          10000  thrpt    5       0.286 ±      0.015  ops/ms
    [info] JavaFactorial.split                  1000  thrpt    5      18.768 ±      4.321  ops/ms
    [info] JavaFactorial.split                 10000  thrpt    5       0.255 ±      0.076  ops/ms

不太明白,你的问题/问题是什么?问题是,如果我的基准测试是随意的,并且由于我尝试测试以外的其他原因而显示加速,那么你的错误意味着什么?它似乎也高出2-3倍。对于这些基准测试,通常
错误
意味着在5次尝试中GC运行1-2次,并影响总分。以下是完整输出运行情况:所以应该是独立的,不必通过链接来获取相关代码。感谢您澄清我的问题!同时,我正在寻找如何验证我的发现,以证明这种加速是由于C2固有的。我看不到一种简单的方法来将加速分离为C2固有的。这是可能的,但您没有o有一个自定义java编译器,它只包含来自JDK-8055494的更改。