这个比较Java8U40和8u31乘以BigInteger值的基准测试是否正确?
我看到8u40更新修复了这个问题,但我没想到它会在2-3倍的时间内加速大整数乘法 以下是在两次JVM更新中通过不同公式计算阶乘的基准测试结果: 8u31这个比较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
[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的更改。