[pypy-commit] pypy improve-rbigint: Some last improvements:
stian
noreply at buildbot.pypy.org
Sat Jul 21 18:42:06 CEST 2012
Author: stian
Branch: improve-rbigint
Changeset: r56373:145006be8e4d
Date: 2012-07-19 02:45 +0200
http://bitbucket.org/pypy/pypy/changeset/145006be8e4d/
Log: Some last improvements:
normalize of a numdigits = 0 doesn't happen. _x_mul with size_a = 1
can still win some performance while it's not power of two using
_muladd1 By passing the size as we know it directly to rbigint() we
save a call (doesn't really add speed, but slightly nicer C code)
fiveary cutoff is benefitial without c (my mistake) annonforceargs
doesn't really change speed (trades a check for a cast in most
cases) Prove numdigits non-negative instead Change div inplace in
floordiv and divmod Fix a potensial issue with floordiv by not
returning self when / 1
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -2,7 +2,7 @@
from pypy.rlib.rarithmetic import ovfcheck, r_longlong, widen, is_valid_int
from pypy.rlib.rarithmetic import most_neg_value_of_same_type
from pypy.rlib.rfloat import isfinite
-from pypy.rlib.debug import make_sure_not_resized, check_regular_int
+from pypy.rlib.debug import make_sure_not_resized, check_regular_int, check_nonneg
from pypy.rlib.objectmodel import we_are_translated, specialize
from pypy.rlib import jit
from pypy.rpython.lltypesystem import lltype, rffi
@@ -112,7 +112,8 @@
"""This is a reimplementation of longs using a list of digits."""
def __init__(self, digits=[NULLDIGIT], sign=0, size=0):
- _check_digits(digits)
+ if not we_are_translated():
+ _check_digits(digits)
make_sure_not_resized(digits)
self._digits = digits
self.size = size or len(digits)
@@ -122,28 +123,27 @@
"""Return the x'th digit, as an int."""
return self._digits[x]
digit._always_inline_ = True
- digit._annonforceargs_ = [None, r_uint] # These are necessary because x can't always be proven non negative, no matter how hard we try.
+
def widedigit(self, x):
"""Return the x'th digit, as a long long int if needed
to have enough room to contain two digits."""
return _widen_digit(self._digits[x])
widedigit._always_inline_ = True
- widedigit._annonforceargs_ = [None, r_uint]
+
def udigit(self, x):
"""Return the x'th digit, as an unsigned int."""
return _load_unsigned_digit(self._digits[x])
udigit._always_inline_ = True
- udigit._annonforceargs_ = [None, r_uint]
+
def setdigit(self, x, val):
val = val & MASK
assert val >= 0
self._digits[x] = _store_digit(val)
setdigit._annspecialcase_ = 'specialize:argtype(2)'
- digit._annonforceargs_ = [None, r_uint, None]
setdigit._always_inline_ = True
def numdigits(self):
- return self.size
+ return check_nonneg(self.size)
numdigits._always_inline_ = True
@staticmethod
@@ -160,7 +160,7 @@
sign = 1
ival = r_uint(intval)
else:
- return rbigint()
+ return NULLRBIGINT
# Count the number of Python digits.
# We used to pick 5 ("big enough for anything"), but that's a
# waste of time and space given that 5*15 = 75 bits are rarely
@@ -170,16 +170,16 @@
carry = ival >> SHIFT
if carry:
return rbigint([_store_digit(ival & MASK),
- _store_digit(carry & MASK)], sign)
+ _store_digit(carry & MASK)], sign, 2)
else:
- return rbigint([_store_digit(ival & MASK)], sign)
+ return rbigint([_store_digit(ival & MASK)], sign, 1)
t = ival
ndigits = 0
while t:
ndigits += 1
t >>= SHIFT
- v = rbigint([NULLDIGIT] * ndigits, sign)
+ v = rbigint([NULLDIGIT] * ndigits, sign, ndigits)
t = ival
p = 0
while t:
@@ -221,9 +221,9 @@
dval = -dval
frac, expo = math.frexp(dval) # dval = frac*2**expo; 0.0 <= frac < 1.0
if expo <= 0:
- return rbigint()
+ return NULLRBIGINT
ndig = (expo-1) // SHIFT + 1 # Number of 'digits' in result
- v = rbigint([NULLDIGIT] * ndig, sign)
+ v = rbigint([NULLDIGIT] * ndig, sign, ndig)
frac = math.ldexp(frac, (expo-1) % SHIFT + 1)
for i in range(ndig-1, -1, -1):
# use int(int(frac)) as a workaround for a CPython bug:
@@ -421,22 +421,20 @@
a, b, asize, bsize = b, a, bsize, asize
if a.sign == 0 or b.sign == 0:
- return rbigint()
+ return NULLRBIGINT
if asize == 1:
if a._digits[0] == NULLDIGIT:
- return rbigint()
+ return NULLRBIGINT
elif a._digits[0] == ONEDIGIT:
return rbigint(b._digits[:], a.sign * b.sign, b.size)
elif bsize == 1:
- result = rbigint([NULLDIGIT] * 2, a.sign * b.sign)
- carry = b.widedigit(0) * a.widedigit(0)
- result.setdigit(0, carry)
- carry >>= SHIFT
+ res = b.widedigit(0) * a.widedigit(0)
+ carry = res >> SHIFT
if carry:
- result.setdigit(1, carry)
- result._normalize()
- return result
+ return rbigint([_store_digit(res & MASK), _store_digit(carry & MASK)], a.sign * b.sign, 2)
+ else:
+ return rbigint([_store_digit(res & MASK)], a.sign * b.sign, 1)
result = _x_mul(a, b, a.digit(0))
elif USE_TOOMCOCK and asize >= TOOMCOOK_CUTOFF:
@@ -469,13 +467,18 @@
if other.numdigits() == 1 and other.sign == 1:
digit = other.digit(0)
if digit == 1:
- return self
+ return rbigint(self._digits[:], other.sign * self.sign, self.size)
elif digit and digit & (digit - 1) == 0:
return self.rshift(ptwotable[digit])
div, mod = _divrem(self, other)
if mod.sign * other.sign == -1:
- div = div.sub(ONERBIGINT)
+ if div.sign == 0:
+ return ONENEGATIVERBIGINT
+ if div.sign == 1:
+ _v_isub(div, 0, div.numdigits(), ONERBIGINT, 1)
+ else:
+ _v_iadd(div, 0, div.numdigits(), ONERBIGINT, 1)
return div
def div(self, other):
@@ -493,12 +496,10 @@
elif digit == 2:
modm = self.digit(0) % digit
if modm:
- if other.sign < 0:
- return ONENEGATIVERBIGINT
- return ONERBIGINT
+ return ONENEGATIVERBIGINT if other.sign == -1 else ONERBIGINT
return NULLRBIGINT
elif digit & (digit - 1) == 0:
- mod = self.and_(_x_sub(other, ONERBIGINT))
+ mod = self.and_(rbigint([_store_digit(digit - 1)], 1, 1))
else:
# Perform
size = self.numdigits() - 1
@@ -513,7 +514,7 @@
if rem == 0:
return NULLRBIGINT
- mod = rbigint([_store_digit(rem)], -1 if self.sign < 0 else 1)
+ mod = rbigint([_store_digit(rem)], -1 if self.sign < 0 else 1, 1)
else:
div, mod = _divrem(self, other)
if mod.sign * other.sign == -1:
@@ -541,7 +542,12 @@
div, mod = _divrem(v, w)
if mod.sign * w.sign == -1:
mod = mod.add(w)
- div = div.sub(ONERBIGINT)
+ if div.sign == 0:
+ return ONENEGATIVERBIGINT, mod
+ if div.sign == 1:
+ _v_isub(div, 0, div.numdigits(), ONERBIGINT, 1)
+ else:
+ _v_iadd(div, 0, div.numdigits(), ONERBIGINT, 1)
return div, mod
@jit.elidable
@@ -611,11 +617,11 @@
# At this point a, b, and c are guaranteed non-negative UNLESS
# c is NULL, in which case a may be negative. */
- z = rbigint([ONEDIGIT], 1)
+ z = rbigint([ONEDIGIT], 1, 1)
# python adaptation: moved macros REDUCE(X) and MULT(X, Y, result)
# into helper function result = _help_mult(x, y, c)
- if not c or size_b <= FIVEARY_CUTOFF:
+ if size_b <= FIVEARY_CUTOFF:
# Left-to-right binary exponentiation (HAC Algorithm 14.79)
# http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
size_b -= 1
@@ -719,13 +725,11 @@
remshift = int_other - wordshift * SHIFT
if not remshift:
- ret = rbigint([NULLDIGIT] * wordshift + self._digits, self.sign)
- ret._normalize()
- return ret
+ return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.size + wordshift)
oldsize = self.numdigits()
newsize = oldsize + wordshift + 1
- z = rbigint([NULLDIGIT] * newsize, self.sign)
+ z = rbigint([NULLDIGIT] * newsize, self.sign, newsize)
accum = _widen_digit(0)
j = 0
while j < oldsize:
@@ -750,7 +754,7 @@
oldsize = self.numdigits()
- z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign)
+ z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign, (oldsize + 1))
accum = _widen_digit(0)
for i in range(oldsize):
@@ -785,7 +789,7 @@
# int is max 63bit, same as our SHIFT now.
#lomask = UDIGIT_MASK((UDIGIT_TYPE(1) << hishift) - 1)
#himask = MASK ^ lomask
- z = rbigint([NULLDIGIT] * newsize, self.sign)
+ z = rbigint([NULLDIGIT] * newsize, self.sign, newsize)
i = 0
while i < newsize:
newdigit = (self.udigit(wordshift) >> loshift) #& lomask
@@ -840,17 +844,12 @@
return l * self.sign
def _normalize(self):
- i = c = self.numdigits()
- if i == 0:
- self.sign = 0
- self.size = 1
- self._digits = [NULLDIGIT]
- return
-
+ i = self.numdigits()
+ # i is always >= 1
while i > 1 and self._digits[i - 1] == NULLDIGIT:
i -= 1
assert i > 0
- if i != c:
+ if i != self.numdigits():
self.size = i
if self.numdigits() == 1 and self._digits[0] == NULLDIGIT:
self.sign = 0
@@ -881,8 +880,8 @@
return "<rbigint digits=%s, sign=%s, %s>" % (self._digits,
self.sign, self.str())
-ONERBIGINT = rbigint([ONEDIGIT], 1)
-ONENEGATIVERBIGINT = rbigint([ONEDIGIT], -1)
+ONERBIGINT = rbigint([ONEDIGIT], 1, 1)
+ONENEGATIVERBIGINT = rbigint([ONEDIGIT], -1, 1)
NULLRBIGINT = rbigint()
#_________________________________________________________________
@@ -1011,7 +1010,7 @@
a, b = b, a
size_a = size_b = i+1
- z = rbigint([NULLDIGIT] * size_a, sign)
+ z = rbigint([NULLDIGIT] * size_a, sign, size_a)
borrow = UDIGIT_TYPE(0)
i = _load_unsigned_digit(0)
while i < size_b:
@@ -1088,9 +1087,13 @@
z._normalize()
return z
- elif digit and digit & (digit - 1) == 0:
- return b.lqshift(ptwotable[digit])
-
+ elif digit:
+ if digit & (digit - 1) == 0:
+ return b.lqshift(ptwotable[digit])
+
+ # Even if it's not power of two it can still be useful.
+ return _muladd1(b, digit)
+
z = rbigint([NULLDIGIT] * (size_a + size_b), 1)
# gradeschool long mult
i = UDIGIT_TYPE(0)
@@ -1123,7 +1126,7 @@
viewing the shift as being by digits. The sign bit is ignored, and
the return values are >= 0.
"""
- size_n = n.numdigits()
+ size_n = n.numdigits() // 3
size_lo = min(size_n, size)
lo = rbigint(n._digits[:size_lo], 1)
mid = rbigint(n._digits[size_lo:size_lo * 2], 1)
@@ -1425,7 +1428,6 @@
size -= 1
while size >= 0:
- assert size >= 0
rem = (rem << SHIFT) + pin.widedigit(size)
hi = rem // n
pout.setdigit(size, hi)
@@ -1442,7 +1444,7 @@
assert n > 0 and n <= MASK
size = a.numdigits()
- z = rbigint([NULLDIGIT] * size, 1)
+ z = rbigint([NULLDIGIT] * size, 1, size)
rem = _inplace_divrem1(z, a, n)
z._normalize()
return z, rem
@@ -1516,7 +1518,7 @@
z.setdigit(i, carry)
z._normalize()
return z
-
+_muladd1._annspecialcase_ = "specialize:argtype(2)"
def _v_lshift(z, a, m, d):
""" Shift digit vector a[0:m] d bits left, with 0 <= d < SHIFT. Put
* result in z[0:m], and return the d bits shifted out of the top.
@@ -1573,7 +1575,8 @@
size_v += 1"""
size_a = size_v - size_w + 1
- a = rbigint([NULLDIGIT] * size_a, 1)
+ assert size_a >= 0
+ a = rbigint([NULLDIGIT] * size_a, 1, size_a)
wm1 = w.widedigit(abs(size_w-1))
wm2 = w.widedigit(abs(size_w-2))
@@ -1745,7 +1748,7 @@
return NULLRBIGINT, a# result is 0
if size_b == 1:
z, urem = _divrem1(a, b.digit(0))
- rem = rbigint([_store_digit(urem)], int(urem != 0))
+ rem = rbigint([_store_digit(urem)], int(urem != 0), 1)
else:
z, rem = _x_divrem(a, b)
# Set the signs.
@@ -2103,7 +2106,7 @@
power += 1
# Get a scratch area for repeated division.
- scratch = rbigint([NULLDIGIT] * size, 1)
+ scratch = rbigint([NULLDIGIT] * size, 1, size)
# Repeatedly divide by powbase.
while 1:
@@ -2200,7 +2203,7 @@
else:
size_z = max(size_a, size_b)
- z = rbigint([NULLDIGIT] * size_z, 1)
+ z = rbigint([NULLDIGIT] * size_z, 1, size_z)
for i in range(size_z):
if i < size_a:
diff --git a/pypy/rlib/test/test_rbigint.py b/pypy/rlib/test/test_rbigint.py
--- a/pypy/rlib/test/test_rbigint.py
+++ b/pypy/rlib/test/test_rbigint.py
@@ -458,8 +458,8 @@
assert x.format('abcdefghijkl', '<<', '>>') == '-<<cakdkgdijffjf>>'
def test_tc_mul(self):
- a = rbigint.fromlong(1<<300)
- b = rbigint.fromlong(1<<200)
+ a = rbigint.fromlong(1<<200)
+ b = rbigint.fromlong(1<<300)
print _tc_mul(a, b)
assert _tc_mul(a, b).tolong() == ((1<<300)*(1<<200))
diff --git a/pypy/translator/goal/targetbigintbenchmark.py b/pypy/translator/goal/targetbigintbenchmark.py
--- a/pypy/translator/goal/targetbigintbenchmark.py
+++ b/pypy/translator/goal/targetbigintbenchmark.py
@@ -36,23 +36,23 @@
Pypy with improvements:
mod by 2: 0.003079
- mod by 10000: 3.227921
- mod by 1024 (power of two): 0.011448
- Div huge number by 2**128: 2.185106
- rshift: 2.327723
- lshift: 1.490478
- Floordiv by 2: 1.555817
- Floordiv by 3 (not power of two): 4.179813
- 2**500000: 0.034017
- (2**N)**5000000 (power of two): 0.047109
- 10000 ** BIGNUM % 100 2.024060
- i = i * i: 3.966529
- n**10000 (not power of two): 6.251766
- Power of two ** power of two: 0.013693
- v = v * power of two 3.535467
- v = v * v 6.361221
- v = v + v 2.771434
- Sum: 39.986681
+ mod by 10000: 3.148599
+ mod by 1024 (power of two): 0.009572
+ Div huge number by 2**128: 2.202237
+ rshift: 2.240624
+ lshift: 1.405393
+ Floordiv by 2: 1.562338
+ Floordiv by 3 (not power of two): 4.197440
+ 2**500000: 0.033737
+ (2**N)**5000000 (power of two): 0.046997
+ 10000 ** BIGNUM % 100 1.321710
+ i = i * i: 3.929341
+ n**10000 (not power of two): 6.215907
+ Power of two ** power of two: 0.014209
+ v = v * power of two 3.506702
+ v = v * v 6.253210
+ v = v + v 2.772122
+ Sum: 38.863216
With SUPPORT_INT128 set to False
mod by 2: 0.004103
More information about the pypy-commit
mailing list