-
Notifications
You must be signed in to change notification settings - Fork 371
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Doubles are capable of holding integral numbers up to 53 bits without losing any precision. The new implementation exploits this fact and uses double to represent long numbers up to 45 bits and then switches to existing number triplet representation for any number beyond that. 45 bits is chosen to simplify the conversion between two representations as the number triplet representation uses two 22bit slots for lower bits. Later the approach can be extended to represent all numbers that fits into 53 bits. The added overhead for big longs is very small. However it uses much less memory for small longs and provides extremely fast arithmetic operations. Benchmarks results shows division is now 5170 times faster in Firefox compared to earlier implementation[1]. The new implementation is also JsInterop friendly as small numbers are real non-opaque numbers usable in javascript side without any conversion. For example, even though time is represented by long in java and double in javascript, both uses less than 45 bits hence now interoperable. I also experimented with doing optimistic arithmetic, meaning that, without any type check I can assume SmallLong and do the arithmethic operation. If it is not a SmallLong the result will never be in the safe-range so we will fallback to BigLong arithmethic. That appraoch provided around another 20% performance improvement for small longs. However surprisingly the overhead for large numbers significantly increased (slows down by half). It doesn't seem like a good trade off so this patches uses explicit type check. [1] Benchmark results: Chrome Firefox IE11 Add(Small): +84.96% +227.51% +7.59% Add(Big): -23.56% -35.74% -59.48% Div(Small): +694.25% +5170.80% +717.81% Div(Big): -8.60% -8.31% -14.12% Mul(Small): +242.78% +785.87% +33.80% Mul(Big): -9.12% -8.55% -38.86% Shr(Small): -34.72% -12.21% -77.41% Shr(Big): -12.23% -16.02% -35.75% Change-Id: I6fcf5650c9357a7255fc126d65f463138fa47e68
- Loading branch information
Showing
9 changed files
with
805 additions
and
496 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,366 @@ | ||
/* | ||
* Copyright 2008 Google Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy of | ||
* the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
package com.google.gwt.lang; | ||
|
||
/** | ||
* Implements a Java <code>long</code> in a way that can be translated to | ||
* JavaScript and could handle numbers needs more than 44 bits. | ||
*/ | ||
class BigLongLib extends BigLongLibBase { | ||
|
||
static class Const { | ||
static final BigLong MAX_VALUE = create(MASK, MASK, MASK_2 >> 1); | ||
static final BigLong MIN_VALUE = create(0, 0, SIGN_BIT_VALUE); | ||
static final BigLong ONE = fromInt(1); | ||
static final BigLong TWO = fromInt(2); | ||
static final BigLong ZERO = fromInt(0); | ||
} | ||
|
||
public static BigLong add(BigLong a, BigLong b) { | ||
int sum0 = getL(a) + getL(b); | ||
int sum1 = getM(a) + getM(b) + (sum0 >> BITS); | ||
int sum2 = getH(a) + getH(b) + (sum1 >> BITS); | ||
|
||
return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2); | ||
} | ||
|
||
public static BigLong and(BigLong a, BigLong b) { | ||
return create(getL(a) & getL(b), getM(a) & getM(b), getH(a) & getH(b)); | ||
} | ||
|
||
public static double compare(BigLong a, BigLong b) { | ||
int signA = sign(a); | ||
int signB = sign(b); | ||
if (signA != signB) { | ||
return signB - signA; | ||
} | ||
|
||
int a2 = getH(a); | ||
int b2 = getH(b); | ||
if (a2 != b2) { | ||
return a2 - b2; | ||
} | ||
|
||
int a1 = getM(a); | ||
int b1 = getM(b); | ||
if (a1 != b1) { | ||
return a1 - b1; | ||
} | ||
|
||
int a0 = getL(a); | ||
int b0 = getL(b); | ||
return a0 - b0; | ||
} | ||
|
||
public static BigLong div(BigLong a, BigLong b) { | ||
return divMod(a, b, false); | ||
} | ||
|
||
public static BigLong fromDouble(double value) { | ||
if (Double.isNaN(value)) { | ||
return Const.ZERO; | ||
} | ||
if (value < -TWO_PWR_63_DBL) { | ||
return Const.MIN_VALUE; | ||
} | ||
if (value >= TWO_PWR_63_DBL) { | ||
return Const.MAX_VALUE; | ||
} | ||
|
||
boolean negative = false; | ||
if (value < 0) { | ||
negative = true; | ||
value = -value; | ||
} | ||
int a2 = 0; | ||
if (value >= TWO_PWR_44_DBL) { | ||
a2 = (int) (value / TWO_PWR_44_DBL); | ||
value -= a2 * TWO_PWR_44_DBL; | ||
} | ||
int a1 = 0; | ||
if (value >= TWO_PWR_22_DBL) { | ||
a1 = (int) (value / TWO_PWR_22_DBL); | ||
value -= a1 * TWO_PWR_22_DBL; | ||
} | ||
int a0 = (int) value; | ||
BigLong result = create(a0, a1, a2); | ||
if (negative) { | ||
negate(result); | ||
} | ||
return result; | ||
} | ||
|
||
public static BigLong fromInt(int value) { | ||
return create(value); | ||
} | ||
|
||
public static long[] getAsLongArray(long l) { | ||
long[] a = new long[3]; | ||
a[0] = (int) (l & MASK); | ||
a[1] = (int) ((l >> BITS) & MASK); | ||
a[2] = (int) ((l >> BITS01) & MASK_2); | ||
return a; | ||
} | ||
|
||
public static BigLong mod(BigLong a, BigLong b) { | ||
divMod(a, b, true); | ||
return remainder; | ||
} | ||
|
||
// Assumes BITS == 22 | ||
public static BigLong mul(BigLong a, BigLong b) { | ||
// Grab 13-bit chunks | ||
int a0 = getL(a) & 0x1fff; | ||
int a1 = (getL(a) >> 13) | ((getM(a) & 0xf) << 9); | ||
int a2 = (getM(a) >> 4) & 0x1fff; | ||
int a3 = (getM(a) >> 17) | ((getH(a) & 0xff) << 5); | ||
int a4 = (getH(a) & 0xfff00) >> 8; | ||
|
||
int b0 = getL(b) & 0x1fff; | ||
int b1 = (getL(b) >> 13) | ((getM(b) & 0xf) << 9); | ||
int b2 = (getM(b) >> 4) & 0x1fff; | ||
int b3 = (getM(b) >> 17) | ((getH(b) & 0xff) << 5); | ||
int b4 = (getH(b) & 0xfff00) >> 8; | ||
|
||
// Compute partial products | ||
// Optimization: if b is small, avoid multiplying by parts that are 0 | ||
int p0 = a0 * b0; // << 0 | ||
int p1 = a1 * b0; // << 13 | ||
int p2 = a2 * b0; // << 26 | ||
int p3 = a3 * b0; // << 39 | ||
int p4 = a4 * b0; // << 52 | ||
|
||
if (b1 != 0) { | ||
p1 += a0 * b1; | ||
p2 += a1 * b1; | ||
p3 += a2 * b1; | ||
p4 += a3 * b1; | ||
} | ||
if (b2 != 0) { | ||
p2 += a0 * b2; | ||
p3 += a1 * b2; | ||
p4 += a2 * b2; | ||
} | ||
if (b3 != 0) { | ||
p3 += a0 * b3; | ||
p4 += a1 * b3; | ||
} | ||
if (b4 != 0) { | ||
p4 += a0 * b4; | ||
} | ||
|
||
// Accumulate into 22-bit chunks: | ||
// .........................................c10|...................c00| | ||
// |....................|..................xxxx|xxxxxxxxxxxxxxxxxxxxxx| p0 | ||
// |....................|......................|......................| | ||
// |....................|...................c11|......c01.............| | ||
// |....................|....xxxxxxxxxxxxxxxxxx|xxxxxxxxx.............| p1 | ||
// |....................|......................|......................| | ||
// |.................c22|...............c12....|......................| | ||
// |..........xxxxxxxxxx|xxxxxxxxxxxxxxxxxx....|......................| p2 | ||
// |....................|......................|......................| | ||
// |.................c23|..c13.................|......................| | ||
// |xxxxxxxxxxxxxxxxxxxx|xxxxx.................|......................| p3 | ||
// |....................|......................|......................| | ||
// |.........c24........|......................|......................| | ||
// |xxxxxxxxxxxx........|......................|......................| p4 | ||
|
||
int c00 = p0 & 0x3fffff; | ||
int c01 = (p1 & 0x1ff) << 13; | ||
int c0 = c00 + c01; | ||
|
||
int c10 = p0 >> 22; | ||
int c11 = p1 >> 9; | ||
int c12 = (p2 & 0x3ffff) << 4; | ||
int c13 = (p3 & 0x1f) << 17; | ||
int c1 = c10 + c11 + c12 + c13; | ||
|
||
int c22 = p2 >> 18; | ||
int c23 = p3 >> 5; | ||
int c24 = (p4 & 0xfff) << 8; | ||
int c2 = c22 + c23 + c24; | ||
|
||
// Propagate high bits from c0 -> c1, c1 -> c2 | ||
c1 += c0 >> BITS; | ||
c0 &= MASK; | ||
c2 += c1 >> BITS; | ||
c1 &= MASK; | ||
c2 &= MASK_2; | ||
|
||
return create(c0, c1, c2); | ||
} | ||
|
||
public static BigLong neg(BigLong a) { | ||
int neg0 = (~getL(a) + 1) & MASK; | ||
int neg1 = (~getM(a) + (neg0 == 0 ? 1 : 0)) & MASK; | ||
int neg2 = (~getH(a) + ((neg0 == 0 && neg1 == 0) ? 1 : 0)) & MASK_2; | ||
|
||
return create(neg0, neg1, neg2); | ||
} | ||
|
||
public static BigLong not(BigLong a) { | ||
return create((~getL(a)) & MASK, (~getM(a)) & MASK, (~getH(a)) & MASK_2); | ||
} | ||
|
||
public static BigLong or(BigLong a, BigLong b) { | ||
return create(getL(a) | getL(b), getM(a) | getM(b), getH(a) | getH(b)); | ||
} | ||
|
||
public static BigLong shl(BigLong a, int n) { | ||
n &= 63; | ||
|
||
int res0, res1, res2; | ||
if (n < BITS) { | ||
res0 = getL(a) << n; | ||
res1 = (getM(a) << n) | (getL(a) >> (BITS - n)); | ||
res2 = (getH(a) << n) | (getM(a) >> (BITS - n)); | ||
} else if (n < BITS01) { | ||
res0 = 0; | ||
res1 = getL(a) << (n - BITS); | ||
res2 = (getM(a) << (n - BITS)) | (getL(a) >> (BITS01 - n)); | ||
} else { | ||
res0 = 0; | ||
res1 = 0; | ||
res2 = getL(a) << (n - BITS01); | ||
} | ||
|
||
return create(res0 & MASK, res1 & MASK, res2 & MASK_2); | ||
} | ||
|
||
public static BigLong shr(BigLong a, int n) { | ||
n &= 63; | ||
|
||
int res0, res1, res2; | ||
|
||
// Sign extend h(a) | ||
int a2 = getH(a); | ||
boolean negative = (a2 & SIGN_BIT_VALUE) != 0; | ||
if (negative) { | ||
a2 |= ~MASK_2; | ||
} | ||
|
||
if (n < BITS) { | ||
res2 = a2 >> n; | ||
res1 = (getM(a) >> n) | (a2 << (BITS - n)); | ||
res0 = (getL(a) >> n) | (getM(a) << (BITS - n)); | ||
} else if (n < BITS01) { | ||
res2 = negative ? MASK_2 : 0; | ||
res1 = a2 >> (n - BITS); | ||
res0 = (getM(a) >> (n - BITS)) | (a2 << (BITS01 - n)); | ||
} else { | ||
res2 = negative ? MASK_2 : 0; | ||
res1 = negative ? MASK : 0; | ||
res0 = a2 >> (n - BITS01); | ||
} | ||
|
||
return create(res0 & MASK, res1 & MASK, res2 & MASK_2); | ||
} | ||
|
||
/** | ||
* Logical right shift. It does not preserve the sign of the input. | ||
*/ | ||
public static BigLong shru(BigLong a, int n) { | ||
n &= 63; | ||
|
||
int res0, res1, res2; | ||
int a2 = getH(a) & MASK_2; | ||
if (n < BITS) { | ||
res2 = a2 >>> n; | ||
res1 = (getM(a) >> n) | (a2 << (BITS - n)); | ||
res0 = (getL(a) >> n) | (getM(a) << (BITS - n)); | ||
} else if (n < BITS01) { | ||
res2 = 0; | ||
res1 = a2 >>> (n - BITS); | ||
res0 = (getM(a) >> (n - BITS)) | (getH(a) << (BITS01 - n)); | ||
} else { | ||
res2 = 0; | ||
res1 = 0; | ||
res0 = a2 >>> (n - BITS01); | ||
} | ||
|
||
return create(res0 & MASK, res1 & MASK, res2 & MASK_2); | ||
} | ||
|
||
public static BigLong sub(BigLong a, BigLong b) { | ||
int sum0 = getL(a) - getL(b); | ||
int sum1 = getM(a) - getM(b) + (sum0 >> BITS); | ||
int sum2 = getH(a) - getH(b) + (sum1 >> BITS); | ||
|
||
return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2); | ||
} | ||
|
||
public static double toDouble(BigLong a) { | ||
if (BigLongLib.compare(a, Const.ZERO) < 0) { | ||
return -toDoubleHelper(BigLongLib.neg(a)); | ||
} | ||
return toDoubleHelper(a); | ||
} | ||
|
||
// Assumes Integer.MIN_VALUE <= a <= Integer.MAX_VALUE | ||
public static int toInt(BigLong a) { | ||
return getL(a) | (getM(a) << BITS); | ||
} | ||
|
||
public static String toString(BigLong a) { | ||
if (BigLongLibBase.isZero(a)) { | ||
return "0"; | ||
} | ||
|
||
if (BigLongLibBase.isMinValue(a)) { | ||
// Special-case MIN_VALUE because neg(MIN_VALUE) == MIN_VALUE | ||
return "-9223372036854775808"; | ||
} | ||
|
||
if (BigLongLibBase.isNegative(a)) { | ||
return "-" + toString(neg(a)); | ||
} | ||
|
||
BigLong rem = a; | ||
String res = ""; | ||
|
||
while (!BigLongLibBase.isZero(rem)) { | ||
// Do several digits each time through the loop, so as to | ||
// minimize the calls to the very expensive emulated div. | ||
final int tenPowerZeroes = 9; | ||
final int tenPower = 1000000000; | ||
BigLong tenPowerLong = fromInt(tenPower); | ||
|
||
rem = divMod(rem, tenPowerLong, true); | ||
String digits = "" + toInt(BigLongLibBase.remainder); | ||
|
||
if (!BigLongLibBase.isZero(rem)) { | ||
int zeroesNeeded = tenPowerZeroes - digits.length(); | ||
for (; zeroesNeeded > 0; zeroesNeeded--) { | ||
digits = "0" + digits; | ||
} | ||
} | ||
|
||
res = digits + res; | ||
} | ||
|
||
return res; | ||
} | ||
|
||
public static BigLong xor(BigLong a, BigLong b) { | ||
return create(getL(a) ^ getL(b), getM(a) ^ getM(b), getH(a) ^ getH(b)); | ||
} | ||
|
||
/** | ||
* Not instantiable. | ||
*/ | ||
private BigLongLib() { | ||
} | ||
} |
Oops, something went wrong.