-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8355719: Reduce memory consumption of BigInteger.pow() #24690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 44 commits
98a5b53
926970e
a3f1489
d280c37
91c3d1a
1cfb775
c25bd32
9c32ce4
7ff919b
1f5a9b4
b6c3320
5d971fa
e459c23
48650de
54ec8f8
3ea5190
6c9b364
3ca7d29
4516d88
b427091
524f195
21fbf27
e11b32f
b527fa2
8de6b82
00365c9
100d0e1
1942fd1
0e1a99e
9cd136c
c3bd1b2
f0d0605
f20d19b
8676af7
3cf820b
23914e8
bf099e4
f9bfd22
7ceae87
cb61ddc
10e122e
b94ca7e
139735d
fcd5d55
b831d01
51272bc
70da95c
d85a634
9a5d696
6033d25
5deb21a
280859b
925806b
ad49b56
6895926
b8ca4fe
4103e49
5ebc16b
e0816d5
009937b
2e08f77
261dd31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1246,6 +1246,16 @@ else if (val < 0 && val >= -MAX_CONSTANT) | |
return new BigInteger(val); | ||
} | ||
|
||
/** | ||
* Constructs a BigInteger with magnitude specified by the long, | ||
* which may not be zero, and the signum specified by the int. | ||
*/ | ||
private BigInteger(long mag, int signum) { | ||
assert mag != 0 && signum != 0; | ||
this.signum = signum; | ||
this.mag = toMagArray(mag); | ||
} | ||
|
||
/** | ||
* Constructs a BigInteger with the specified value, which may not be zero. | ||
*/ | ||
|
@@ -1256,16 +1266,14 @@ private BigInteger(long val) { | |
} else { | ||
signum = 1; | ||
} | ||
mag = toMagArray(val); | ||
} | ||
|
||
int highWord = (int)(val >>> 32); | ||
if (highWord == 0) { | ||
mag = new int[1]; | ||
mag[0] = (int)val; | ||
} else { | ||
mag = new int[2]; | ||
mag[0] = highWord; | ||
mag[1] = (int)val; | ||
} | ||
private static int[] toMagArray(long mag) { | ||
int highWord = (int) (mag >>> 32); | ||
return highWord == 0 | ||
? new int[] { (int) mag } | ||
: new int[] { highWord, (int) mag }; | ||
} | ||
|
||
/** | ||
|
@@ -2589,17 +2597,19 @@ public BigInteger pow(int exponent) { | |
if (exponent < 0) { | ||
throw new ArithmeticException("Negative exponent"); | ||
} | ||
if (signum == 0) { | ||
return (exponent == 0 ? ONE : this); | ||
} | ||
if (exponent == 0 || this.equals(ONE)) | ||
return ONE; | ||
|
||
BigInteger partToSquare = this.abs(); | ||
if (signum == 0) | ||
return ZERO; | ||
|
||
BigInteger base = this.abs(); | ||
|
||
// Factor out powers of two from the base, as the exponentiation of | ||
// these can be done by left shifts only. | ||
// The remaining part can then be exponentiated faster. The | ||
// powers of two will be multiplied back at the end. | ||
int powersOfTwo = partToSquare.getLowestSetBit(); | ||
int powersOfTwo = base.getLowestSetBit(); | ||
long bitsToShiftLong = (long)powersOfTwo * exponent; | ||
if (bitsToShiftLong > Integer.MAX_VALUE) { | ||
reportOverflow(); | ||
|
@@ -2610,8 +2620,8 @@ public BigInteger pow(int exponent) { | |
|
||
// Factor the powers of two out quickly by shifting right, if needed. | ||
if (powersOfTwo > 0) { | ||
partToSquare = partToSquare.shiftRight(powersOfTwo); | ||
remainingBits = partToSquare.bitLength(); | ||
base = base.shiftRight(powersOfTwo); | ||
remainingBits = base.bitLength(); | ||
if (remainingBits == 1) { // Nothing left but +/- 1? | ||
if (signum < 0 && (exponent&1) == 1) { | ||
fabioromano1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return NEGATIVE_ONE.shiftLeft(bitsToShift); | ||
|
@@ -2620,7 +2630,7 @@ public BigInteger pow(int exponent) { | |
} | ||
} | ||
} else { | ||
remainingBits = partToSquare.bitLength(); | ||
remainingBits = base.bitLength(); | ||
if (remainingBits == 1) { // Nothing left but +/- 1? | ||
if (signum < 0 && (exponent&1) == 1) { | ||
return NEGATIVE_ONE; | ||
|
@@ -2636,57 +2646,39 @@ public BigInteger pow(int exponent) { | |
long scaleFactor = (long)remainingBits * exponent; | ||
|
||
// Use slightly different algorithms for small and large operands. | ||
// See if the result will safely fit into a long. (Largest 2^63-1) | ||
if (partToSquare.mag.length == 1 && scaleFactor <= 62) { | ||
// Small number algorithm. Everything fits into a long. | ||
// See if the result will safely fit into an unsigned long. (Largest 2^64-1) | ||
if (base.mag.length == 1 && scaleFactor <= Long.SIZE) { | ||
// Small number algorithm. Everything fits into an unsigned long. | ||
int newSign = (signum <0 && (exponent&1) == 1 ? -1 : 1); | ||
long result = 1; | ||
long baseToPow2 = partToSquare.mag[0] & LONG_MASK; | ||
|
||
int workingExponent = exponent; | ||
|
||
// Perform exponentiation using repeated squaring trick | ||
while (workingExponent != 0) { | ||
if ((workingExponent & 1) == 1) { | ||
result = result * baseToPow2; | ||
} | ||
|
||
if ((workingExponent >>>= 1) != 0) { | ||
baseToPow2 = baseToPow2 * baseToPow2; | ||
} | ||
} | ||
long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); | ||
|
||
// Multiply back the powers of two (quickly, by shifting left) | ||
if (powersOfTwo > 0) { | ||
if (bitsToShift + scaleFactor <= 62) { // Fits in long? | ||
return valueOf((result << bitsToShift) * newSign); | ||
} else { | ||
return valueOf(result*newSign).shiftLeft(bitsToShift); | ||
} | ||
} else { | ||
return valueOf(result*newSign); | ||
} | ||
return powersOfTwo == 0 | ||
fabioromano1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
? new BigInteger(result, newSign) | ||
: (bitsToShift + scaleFactor <= Long.SIZE // Fits in long? | ||
? new BigInteger(result << bitsToShift, newSign) | ||
: new BigInteger(result, newSign).shiftLeft(bitsToShift)); | ||
} else { | ||
if ((long)bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { | ||
reportOverflow(); | ||
} | ||
|
||
// Large number algorithm. This is basically identical to | ||
// the algorithm above, but calls multiply() and square() | ||
// the algorithm above, but calls multiply() | ||
// which may use more efficient algorithms for large numbers. | ||
BigInteger answer = ONE; | ||
|
||
int workingExponent = exponent; | ||
final int expZeros = Integer.numberOfLeadingZeros(exponent); | ||
int workingExp = exponent << expZeros; | ||
// Perform exponentiation using repeated squaring trick | ||
while (workingExponent != 0) { | ||
if ((workingExponent & 1) == 1) { | ||
answer = answer.multiply(partToSquare); | ||
} | ||
for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { | ||
answer = answer.multiply(answer); | ||
fabioromano1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (workingExp < 0) // leading bit is set | ||
answer = answer.multiply(base); | ||
|
||
if ((workingExponent >>>= 1) != 0) { | ||
partToSquare = partToSquare.square(); | ||
} | ||
workingExp <<= 1; | ||
} | ||
|
||
// Multiply back the (exponentiated) powers of two (quickly, | ||
// by shifting left) | ||
if (powersOfTwo > 0) { | ||
|
@@ -2701,6 +2693,88 @@ public BigInteger pow(int exponent) { | |
} | ||
} | ||
|
||
/** | ||
* Computes {@code x^n} using repeated squaring trick. | ||
* Assumes {@code x != 0 && x^n < 2^Long.SIZE}. | ||
*/ | ||
static long unsignedLongPow(long x, int n) { | ||
if (x == 1L) | ||
return 1L; | ||
|
||
if (x == 2L) | ||
return 1L << n; | ||
// Double.PRECISION / bitLength(x) is the largest integer e | ||
// such that x^e fits into a double. If e <= 3, we won't use fp arithmetic. | ||
// This allows to use fp arithmetic in computePower(), where possible. | ||
final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); | ||
final int maxExpLen = bitLengthForInt(maxExp); | ||
final long[] powerCache = new long[1 << maxExpLen]; | ||
|
||
final int nZeros = Integer.numberOfLeadingZeros(n); | ||
n <<= nZeros; | ||
|
||
long pow = 1L; | ||
int blockLen; | ||
for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { | ||
blockLen = Math.min(maxExpLen, nLen); | ||
// compute pow^(2^blockLen) | ||
if (pow != 1L) { | ||
for (int i = 0; i < blockLen; i++) | ||
pow *= pow; | ||
} | ||
|
||
// add exp to power's exponent | ||
int exp = n >>> -blockLen; | ||
if (exp > 0) | ||
pow *= computePower(powerCache, x, exp, maxExp); | ||
|
||
n <<= blockLen; // shift to next block of bits | ||
} | ||
|
||
return pow; | ||
fabioromano1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Returns {@code x^exp}. This method is used by {@code unsignedLongPow(long, int)}. | ||
* Assumes {@code maxExp == max(3, Double.PRECISION / bitLength(x))}. | ||
*/ | ||
private static long computePower(long[] powerCache, long x, int exp, int maxExp) { | ||
long xToExp = powerCache[exp]; | ||
if (xToExp == 0) { | ||
if (exp <= maxExp) { | ||
// don't use fp arithmetic if exp <= 3 | ||
xToExp = exp == 1 ? x : | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about pre-seeding the cache with values for 1, 2 and 3? That would allow unconditionally using fp here (and below) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's a good idea, but doing so would increase the already high dependence of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't pre-seeding involve extra cpu cycles which are defintely wasted if the results aren't used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It would involve just 3 assignments and 3 multiplications of 64-bit words, I think the overhead is negligible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case you can still use computePower and remove the branching to check if double arithmetic should be used - you are passing the cache array, so you can control what is the initial values in that array. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, but if I have to check the contents of the cache, then it's useless to remove the branching to check if double arithmetic should be used, since the same check has to be done previously. |
||
(exp == 2 ? x*x : | ||
(exp == 3 ? x*x*x : (long) Math.pow(x, exp))); | ||
powerCache[exp] = xToExp; | ||
} else { | ||
// adjust exp to fit x^expAdj into a double | ||
final int expAdj = exp >>> 1; | ||
|
||
xToExp = powerCache[expAdj << 1]; | ||
if (xToExp == 0) { // compute x^(expAdj << 1) | ||
xToExp = powerCache[expAdj]; | ||
if (xToExp == 0) { // compute x^expAdj | ||
// don't use fp arithmetic if expAdj <= 3 | ||
xToExp = expAdj == 1 ? x : | ||
(expAdj == 2 ? x*x : | ||
(expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); | ||
powerCache[expAdj] = xToExp; | ||
} | ||
xToExp *= xToExp; | ||
powerCache[expAdj << 1] = xToExp; | ||
} | ||
|
||
// append exp's rightmost bit to expAdj | ||
if ((exp & 1) == 1) { | ||
xToExp *= x; | ||
powerCache[exp] = xToExp; | ||
} | ||
} | ||
} | ||
return xToExp; | ||
} | ||
|
||
/** | ||
* Returns the integer square root of this BigInteger. The integer square | ||
* root of the corresponding mathematical integer {@code n} is the largest | ||
|
Uh oh!
There was an error while loading. Please reload this page.