Author: Carl Friedrich Bolz <[email protected]>
Branch: 
Changeset: r65448:7ceb58dbdc24
Date: 2013-07-17 20:24 +0200
http://bitbucket.org/pypy/pypy/changeset/7ceb58dbdc24/

Log:    make small str(small long) much faster by caching the powers of ten
        needed

diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -2052,6 +2052,23 @@
 
 _FORMAT_MINDIGITS = 5 # 36 ** 5 fits in 32 bits, there may be a better choice 
for this
 
+class _PartsCache(object):
+    def __init__(self):
+        # 36 - 3, because bases 0, 1 make no sense
+        # and 2 is handled differently
+        self.parts_cache = [None] * 33
+
+    def get_cached_parts(self, base):
+        res = self.parts_cache[base - 3]
+        if res is None:
+            rbase = rbigint.fromint(base)
+            part = rbase.pow(rbigint.fromint(_FORMAT_MINDIGITS))
+            res = [part]
+            self.parts_cache[base - 3] = res
+        return res
+
+_parts_cache = _PartsCache()
+
 def _format_int(val, digits):
     base = len(digits)
     out = []
@@ -2068,9 +2085,7 @@
     if i < 0:
         # this checks whether any digit has been appended yet
         if output.getlength() == size_prefix:
-            if x.sign == 0:
-                pass
-            else:
+            if x.sign != 0:
                 s = _format_int(x.toint(), digits)
                 output.append(s)
         else:
@@ -2095,22 +2110,34 @@
     rbase = rbigint.fromint(base)
     two = rbigint.fromint(2)
 
-    pts = [rbase.pow(rbigint.fromint(_FORMAT_MINDIGITS))]
+    pts = _parts_cache.get_cached_parts(base)
     stringsize = _FORMAT_MINDIGITS
-    while pts[-1].lt(x):
-        pts.append(pts[-1].pow(two))
-        stringsize *= 2
-    pts.pop() # remove first base**2**i greater than x
+    startindex = 0
+    for startindex, part in enumerate(pts):
+        if not part.lt(x):
+            break
+        stringsize *= 2 # XXX can this overflow on 32 bit?
+    else:
+        # not enough parts computed yet
+        while pts[-1].lt(x):
+            pts.append(pts[-1].pow(two))
+            stringsize *= 2
+
+        startindex = len(pts) - 1
+
+    # remove first base**2**i greater than x
+    startindex -= 1
 
     output = StringBuilder(stringsize)
     if negative:
         output.append('-')
     output.append(prefix)
-    _format_recursive(x, len(pts)-1, output, pts, digits, output.getlength())
+    _format_recursive(x, startindex, output, pts, digits, output.getlength())
 
     output.append(suffix)
     return output.build()
 
+
 def _bitwise(a, op, b): # '&', '|', '^'
     """ Bitwise and/or/xor operations """
 
diff --git a/rpython/rlib/test/test_rbigint.py 
b/rpython/rlib/test/test_rbigint.py
--- a/rpython/rlib/test/test_rbigint.py
+++ b/rpython/rlib/test/test_rbigint.py
@@ -515,7 +515,19 @@
         assert x.format('.!') == (
             '-!....!!..!!..!.!!.!......!...!...!!!........!')
         assert x.format('abcdefghijkl', '<<', '>>') == '-<<cakdkgdijffjf>>'
-        
+
+    def test_format_caching(self):
+        big = rbigint.fromlong(2 ** 1000)
+        rbigint.pow = None
+        res1 = big.str()
+        oldpow = rbigint.__dict__['pow']
+        # make sure pow is not used the second time
+        try:
+            res2 = big.str()
+            assert res2 == res1
+        finally:
+            rbigint.pow = oldpow
+
     def test_overzelous_assertion(self):
         a = rbigint.fromlong(-1<<10000)
         b = rbigint.fromlong(-1<<3000)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to