Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r64280:5de7c3413954
Date: 2013-05-18 11:41 +0200
http://bitbucket.org/pypy/pypy/changeset/5de7c3413954/

Log:    Build the final digits in order rather than in opposite order, with
        a regular builder. Use unrolling.

diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -5,6 +5,7 @@
 from rpython.rlib.rstring import StringBuilder
 from rpython.rlib.debug import make_sure_not_resized, check_regular_int
 from rpython.rlib.objectmodel import we_are_translated, specialize
+from rpython.rlib.unroll import unrolling_iterable
 from rpython.rlib import jit
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper import extregistry
@@ -2106,8 +2107,30 @@
     DECIMAL_SHIFT += 1
 DECIMAL_BASE = 10 ** DECIMAL_SHIFT
 
+# an RPython trick: this creates a nested sequence of calls that are
+# all inlined into each other, making an unrolled loop.  Moreover the
+# calls are done in the "wrong" order to write it as a regular loop:
+# the first digit that is append-ed to the builder is the most
+# significant one (corresponding to the innermost call).
[email protected]()
+def _minus_1(x):
+    return x - 1
[email protected](2)
+def _add_decimal_digits(builder, value, ndigits):
+    assert value >= 0
+    if ndigits > 1:
+        _add_decimal_digits(builder, value // 10, _minus_1(ndigits))
+        builder.append(chr(ord('0') + value % 10))
+    else:
+        assert value < 10
+        builder.append(chr(ord('0') + value))
+_add_decimal_digits._always_inline_ = True
+
+count_decimal_shift_from_1 = unrolling_iterable(range(1, DECIMAL_SHIFT))
+
+
 def _format_decimal(a, addL=False):
-    """ Optimized version of _format(a, BASE10, '', addL*'L'). """
+    """ Optimized version of _format(a, BASE10, '', 'L' if addL else ''). """
     if a.sign == 0:
         if addL:
             return "0L"
@@ -2148,49 +2171,41 @@
     assert sizem1 >= 0
 
     # calculate exact length of output string, and allocate
+    rem = pout[sizem1]
+    tenpow = 10
+    for i in count_decimal_shift_from_1:
+        if rem < tenpow:
+            decimal_digits_in_last_part = i
+            break
+        tenpow *= 10
+    else:
+        decimal_digits_in_last_part = DECIMAL_SHIFT
     strlen = (addL + negative +
-              1 + (sizem1) * DECIMAL_SHIFT)
-    tenpow = 10
-    rem = pout[sizem1]
-    while rem >= tenpow:
-        tenpow *= 10
-        strlen += 1
+              decimal_digits_in_last_part + (sizem1) * DECIMAL_SHIFT)
 
-    l = ['\x00'] * strlen
+    builder = StringBuilder(strlen)
 
-    p = strlen
-    if addL:
-        p -= 1; assert p >= 0
-        l[p] = 'L'
+    # start with the negative sign, if needed
+    if negative:
+        builder.append('-')
+
+    # pout[size-1]: always produce at least one decimal digit
+    for i in count_decimal_shift_from_1:
+        if i == decimal_digits_in_last_part:
+            _add_decimal_digits(builder, pout[sizem1], i)
+            break
+    else:
+        _add_decimal_digits(builder, pout[sizem1], DECIMAL_SHIFT)
 
     # pout[0] through pout[size-2] contribute exactly
     # DECIMAL_SHIFT digits each
-    for i in range(sizem1):
-        rem = pout[i]
-        assert rem >= 0
-        for j in range(DECIMAL_SHIFT):
-            p -= 1; assert p >= 0
-            l[p] = chr(ord('0') + rem % 10)
-            rem //= 10
+    for i in range(sizem1-1, -1, -1):
+        _add_decimal_digits(builder, pout[i], DECIMAL_SHIFT)
 
-    # pout[size-1]: always produce at least one decimal digit
-    rem = pout[sizem1]
-    assert rem >= 0
-    while True:
-        p -= 1; assert p >= 0
-        l[p] = chr(ord('0') + rem % 10)
-        if rem < 10:
-            break
-        rem //= 10
-
-    # and sign
-    if negative:
-        p -= 1; assert p >= 0
-        l[p] = '-'
-
-    # check we've counted correctly
-    assert p == 0
-    return ''.join(l)
+    # done
+    if addL:
+        builder.append('L')
+    return builder.build()
 
 
 def _bitwise(a, op, b): # '&', '|', '^'
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to