[pypy-commit] pypy improve-rbigint: Add special cases for 0, 1 and power of two multiplication.

stian noreply at buildbot.pypy.org
Sat Jul 21 18:40:56 CEST 2012


Author: stian
Branch: improve-rbigint
Changeset: r56311:0624736de0b2
Date: 2012-06-22 01:09 +0200
http://bitbucket.org/pypy/pypy/changeset/0624736de0b2/

Log:	Add special cases for 0, 1 and power of two multiplication.

	Increase both general multiplications like this: for n in
	xrange(10000): rbigint.pow(rbigint.fromint(n),
	rbigint.fromint(10**4))

	And: for n in xrange(100000):
	rbigint.pow(rbigint.fromint(1024), rbigint.fromint(1024))

	By 6-7%.

diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -789,6 +789,7 @@
             sign = -1
             a, b = b, a
         size_a = size_b = i+1
+        
     z = rbigint([NULLDIGIT] * size_a, sign)
     borrow = r_uint(0)
     i = 0
@@ -810,7 +811,12 @@
     z._normalize()
     return z
 
-
+# A neat little table of power of twos.
+ptwotable = {}
+for x in range(SHIFT):
+    ptwotable[2 << x] = x+1
+    ptwotable[-2 << x] = x+1
+    
 def _x_mul(a, b):
     """
     Grade school multiplication, ignoring the signs.
@@ -819,7 +825,6 @@
 
     size_a = a.numdigits()
     size_b = b.numdigits()
-    z = rbigint([NULLDIGIT] * (size_a + size_b), 1)
     """
     # Code below actually runs slower (about 20%). Dunno why, since it shouldn't.
     if a is b:
@@ -861,6 +866,17 @@
             assert (carry >> SHIFT) == 0
             i += 1
     else:"""
+    if size_a == 1:
+        # Special case.
+        digit = a.digit(0)
+        if digit == 0:
+            return rbigint(a._digits[:], 1)
+        elif digit == 1:
+            return rbigint(b._digits[:], 1)
+        elif digit & (digit - 1) == 0:
+            return b.lshift(ptwotable[digit])
+    
+    z = rbigint([NULLDIGIT] * (size_a + size_b), 1)
     # gradeschool long mult
     i = 0
     while i < size_a:
@@ -931,6 +947,10 @@
         else:
             return _x_mul(a, b)
 
+    if asize == 1:
+        # Then _x_mul will always be faster.
+        return _x_mul(a, b)
+    
     # If a is small compared to b, splitting on b gives a degenerate
     # case with ah==0, and Karatsuba may be (even much) less efficient
     # than "grade school" then.  However, we can still win, by viewing
@@ -1135,6 +1155,7 @@
     The sign of a is ignored; n should not be zero.
     """
     assert n > 0 and n <= MASK
+        
     size = a.numdigits()
     z = rbigint([NULLDIGIT] * size, 1)
     rem = _inplace_divrem1(z, a, n)
@@ -1198,6 +1219,11 @@
 def _muladd1(a, n, extra=0):
     """Multiply by a single digit and add a single digit, ignoring the sign.
     """
+    
+    # Special case this one. 
+    if n == 1 and not extra:
+        return a
+
     size_a = a.numdigits()
     z = rbigint([NULLDIGIT] * (size_a+1), 1)
     assert extra & MASK == extra


More information about the pypy-commit mailing list