Peter Levart posted in OpenJDK:
------------------------------------------
I think (haven't tried, just speculate) you could achieve the same performance by:
- adding final qualifier to static BigInteger.[posConst|negConst] fields
- annotating those fields with @jdk.internal.vm.annotation.Stable annotation
This way BigInteger.valueOf(-MAX_CONSTANT <= i <= MAX_CONSTANT) when called with a constant argument should fold into a constant when compiled by JIT.
The same optimization could be performed for valueOf methods of java.lang.Byte, Character, Short, Integer & Long.
@Stable annotation was a package-private annotation in java.lang.invoke, reserved for method handles infrastructure, but has since been made public and moved to a concealed package of java.base. There is already a precedent for its use outside in java.lang.invoke: in java.lang.String. For example:
static final String s = ".....";
s.charAt(0); // is folded into a constant by JIT
Peter Levart evaluation in OpenJDK:
----------------------------------------------
Ok, here's a test...
with just the following change:
diff -r 9ea9fb3c0c88 src/java.base/share/classes/java/math/BigInteger.java
--- a/src/java.base/share/classes/java/math/BigInteger.java Wed Mar 23 18:24:35 2016 +0100
+++ b/src/java.base/share/classes/java/math/BigInteger.java Wed Mar 23 19:55:01 2016 +0100
@@ -41,6 +41,7 @@
import jdk.internal.math.DoubleConsts;
import jdk.internal.math.FloatConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.vm.annotation.Stable;
/**
* Immutable arbitrary-precision integers. All operations behave as if
@@ -1213,8 +1214,10 @@
* Initialize static constant array when class is loaded.
*/
private static final int MAX_CONSTANT = 16;
- private static BigInteger posConst[] = new BigInteger[MAX_CONSTANT+1];
- private static BigInteger negConst[] = new BigInteger[MAX_CONSTANT+1];
+ @Stable
+ private static final BigInteger posConst[] = new BigInteger[MAX_CONSTANT+1];
+ @Stable
+ private static final BigInteger negConst[] = new BigInteger[MAX_CONSTANT+1];
/**
* The cache of powers of each radix. This allows us to not have to
The results of simple benchmark:
/*
Original:
Benchmark Mode Cnt Score Error Units
BigIntegerBench.ONE avgt 10 2.396 �� 0.232 ns/op
BigIntegerBench.valueOf_1 avgt 10 2.846 �� 0.233 ns/op
BigIntegerBench.valueOf_2 avgt 10 2.808 �� 0.054 ns/op
Patched:
Benchmark Mode Cnt Score Error Units
BigIntegerBench.ONE avgt 10 2.381 �� 0.126 ns/op
BigIntegerBench.valueOf_1 avgt 10 2.347 �� 0.089 ns/op
BigIntegerBench.valueOf_2 avgt 10 2.323 �� 0.022 ns/op
*/
package jdk.test;
import org.openjdk.jmh.annotations.*;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 10)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class BigIntegerBench {
@Benchmark
public BigInteger ONE() {
return BigInteger.ONE;
}
@Benchmark
public BigInteger valueOf_1() {
return BigInteger.valueOf(1);
}
@Benchmark
public BigInteger valueOf_2() {
return BigInteger.valueOf(2);
}
}
So, no need to change the API and all uses of valueOf(-MAX_CONSTANT <= i <= MAX_CONSTANT) for constant 'i' will be faster a bit.