]> Cypherpunks repositories - gostls13.git/commitdiff
strconv: optimize decimal to string conversion
authorMartin Möhrmann <martisch@uos.de>
Thu, 25 Dec 2014 18:30:53 +0000 (19:30 +0100)
committerRobert Griesemer <gri@golang.org>
Tue, 30 Dec 2014 23:28:02 +0000 (23:28 +0000)
Avoid the decimal lookup in digits array and compute the decimal character value directly.
Reduce calls to 64bit division on 32bit plattforms by splitting conversion into smaller blocks.
Convert value to uintptr type when it can be represented by uintptr.

on darwin/386

benchmark               old ns/op     new ns/op     delta
BenchmarkFormatInt      8352          7466          -10.61%
BenchmarkAppendInt      4281          3401          -20.56%
BenchmarkFormatUint     2785          2251          -19.17%
BenchmarkAppendUint     1770          1223          -30.90%

on darwin/amd64

benchmark               old ns/op     new ns/op     delta
BenchmarkFormatInt      5531          5492          -0.71%
BenchmarkAppendInt      2435          2295          -5.75%
BenchmarkFormatUint     1628          1569          -3.62%
BenchmarkAppendUint     726           750           +3.31%

Change-Id: Ifca281cbdd62ab7d7bd4b077a96da99eb12cf209
Reviewed-on: https://go-review.googlesource.com/2105
Reviewed-by: Robert Griesemer <gri@golang.org>
src/strconv/itoa.go

index 67f17d866474b462f2b42140c2649ba4f2586b95..e6f6303356b99e58165b029d6df7ffd2f72141bd 100644 (file)
@@ -40,9 +40,7 @@ func AppendUint(dst []byte, i uint64, base int) []byte {
 }
 
 const (
-       digits   = "0123456789abcdefghijklmnopqrstuvwxyz"
-       digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
-       digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+       digits = "0123456789abcdefghijklmnopqrstuvwxyz"
 )
 
 var shifts = [len(digits) + 1]uint{
@@ -74,23 +72,34 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s
 
        // convert bits
        if base == 10 {
-               // common case: use constants for / and % because
-               // the compiler can optimize it into a multiply+shift,
-               // and unroll loop
-               for u >= 100 {
-                       i -= 2
-                       q := u / 100
-                       j := uintptr(u - q*100)
-                       a[i+1] = digits01[j]
-                       a[i+0] = digits10[j]
-                       u = q
+               // common case: use constants for / because
+               // the compiler can optimize it into a multiply+shift
+
+               if ^uintptr(0)>>32 == 0 {
+                       for u > uint64(^uintptr(0)) {
+                               q := u / 1e9
+                               us := uintptr(u - q*1e9) // us % 1e9 fits into a uintptr
+                               for j := 9; j > 0; j-- {
+                                       i--
+                                       qs := us / 10
+                                       a[i] = byte(us - qs*10 + '0')
+                                       us = qs
+                               }
+                               u = q
+                       }
                }
-               if u >= 10 {
+
+               // u guaranteed to fit into a uintptr
+               us := uintptr(u)
+               for us >= 10 {
                        i--
-                       q := u / 10
-                       a[i] = digits[uintptr(u-q*10)]
-                       u = q
+                       q := us / 10
+                       a[i] = byte(us - q*10 + '0')
+                       us = q
                }
+               // u < 10
+               i--
+               a[i] = byte(us + '0')
 
        } else if s := shifts[base]; s > 0 {
                // base is power of 2: use shifts and masks instead of / and %
@@ -101,21 +110,24 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s
                        a[i] = digits[uintptr(u)&m]
                        u >>= s
                }
+               // u < base
+               i--
+               a[i] = digits[uintptr(u)]
 
        } else {
                // general case
                b := uint64(base)
                for u >= b {
                        i--
-                       a[i] = digits[uintptr(u%b)]
-                       u /= b
+                       q := u / b
+                       a[i] = digits[uintptr(u-q*b)]
+                       u = q
                }
+               // u < base
+               i--
+               a[i] = digits[uintptr(u)]
        }
 
-       // u < base
-       i--
-       a[i] = digits[uintptr(u)]
-
        // add sign, if any
        if neg {
                i--