]> Cypherpunks repositories - gostls13.git/commitdiff
strconv: use 64bit uint for decimal conversion if available
authorRobert Griesemer <gri@golang.org>
Tue, 3 Feb 2015 01:22:18 +0000 (17:22 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 10 Apr 2015 17:42:20 +0000 (17:42 +0000)
The existing code used ints for the (slow) decimal conversion and
assumed that they were 32bit wide.

This change uses uints and the appropriate width (32 or 64bit)
depending on platform.

The performance difference is in the noise for the usual (optimized)
case which does not use the slow path conversion:

benchmark                               old ns/op     new ns/op     delta
BenchmarkFormatFloatDecimal             298           299           +0.34%
BenchmarkFormatFloat                    388           392           +1.03%
BenchmarkFormatFloatExp                 365           364           -0.27%
BenchmarkFormatFloatNegExp              364           362           -0.55%
BenchmarkFormatFloatBig                 482           476           -1.24%
BenchmarkAppendFloatDecimal             100           102           +2.00%
BenchmarkAppendFloat                    199           201           +1.01%
BenchmarkAppendFloatExp                 174           175           +0.57%
BenchmarkAppendFloatNegExp              169           174           +2.96%
BenchmarkAppendFloatBig                 286           286           +0.00%
BenchmarkAppendFloat32Integer           99.9          102           +2.10%
BenchmarkAppendFloat32ExactFraction     161           164           +1.86%
BenchmarkAppendFloat32Point             199           201           +1.01%
BenchmarkAppendFloat32Exp               167           168           +0.60%
BenchmarkAppendFloat32NegExp            163           169           +3.68%
BenchmarkAppendFloat64Fixed1            137           134           -2.19%
BenchmarkAppendFloat64Fixed2            144           146           +1.39%
BenchmarkAppendFloat64Fixed3            138           140           +1.45%
BenchmarkAppendFloat64Fixed4            144           145           +0.69%

The performance difference is significant if the fast path conversion is
explicitly turned off (ftoa.go:101):

benchmark                               old ns/op     new ns/op     delta
BenchmarkFormatFloatDecimal             459           427           -6.97%
BenchmarkFormatFloat                    1560          1180          -24.36%
BenchmarkFormatFloatExp                 5501          3128          -43.14%
BenchmarkFormatFloatNegExp              24085         14360         -40.38%
BenchmarkFormatFloatBig                 1409          1081          -23.28%
BenchmarkAppendFloatDecimal             248           226           -8.87%
BenchmarkAppendFloat                    1315          982           -25.32%
BenchmarkAppendFloatExp                 5274          2869          -45.60%
BenchmarkAppendFloatNegExp              23905         14054         -41.21%
BenchmarkAppendFloatBig                 1194          860           -27.97%
BenchmarkAppendFloat32Integer           167           175           +4.79%
BenchmarkAppendFloat32ExactFraction     182           184           +1.10%
BenchmarkAppendFloat32Point             556           564           +1.44%
BenchmarkAppendFloat32Exp               1134          918           -19.05%
BenchmarkAppendFloat32NegExp            2679          1801          -32.77%
BenchmarkAppendFloat64Fixed1            274           238           -13.14%
BenchmarkAppendFloat64Fixed2            494           368           -25.51%
BenchmarkAppendFloat64Fixed3            1833          1008          -45.01%
BenchmarkAppendFloat64Fixed4            6133          3596          -41.37%

Change-Id: I829b8abcca882b1c10d8ae421d3249597c31f3c9
Reviewed-on: https://go-review.googlesource.com/3811
Reviewed-by: Russ Cox <rsc@golang.org>
src/strconv/decimal.go

index 3d7c8d1da97ef1a3e3167abdbca4bdca32dc2bc3..5252d6e86eb66bc866e4d28c276e457cb1de181c 100644 (file)
@@ -102,8 +102,9 @@ func (a *decimal) Assign(v uint64) {
 }
 
 // Maximum shift that we can do in one pass without overflow.
-// Signed int has 31 bits, and we have to be able to accommodate 9<<k.
-const maxShift = 27
+// A uint has 32 or 64 bits, and we have to be able to accommodate 9<<k.
+const uintSize = 32 << (^uint(0) >> 63)
+const maxShift = uintSize - 4
 
 // Binary shift right (/ 2) by k bits.  k <= maxShift to avoid overflow.
 func rightShift(a *decimal, k uint) {
@@ -111,7 +112,7 @@ func rightShift(a *decimal, k uint) {
        w := 0 // write pointer
 
        // Pick up enough leading digits to cover first shift.
-       n := 0
+       var n uint
        for ; n>>k == 0; r++ {
                if r >= a.nd {
                        if n == 0 {
@@ -125,14 +126,14 @@ func rightShift(a *decimal, k uint) {
                        }
                        break
                }
-               c := int(a.d[r])
+               c := uint(a.d[r])
                n = n*10 + c - '0'
        }
        a.dp -= r - 1
 
        // Pick up a digit, put down a digit.
        for ; r < a.nd; r++ {
-               c := int(a.d[r])
+               c := uint(a.d[r])
                dig := n >> k
                n -= dig << k
                a.d[w] = byte(dig + '0')
@@ -169,50 +170,84 @@ func rightShift(a *decimal, k uint) {
 
 type leftCheat struct {
        delta  int    // number of new digits
-       cutoff string //   minus one digit if original < a.
+       cutoff string // minus one digit if original < a.
 }
 
 var leftcheats = []leftCheat{
        // Leading digits of 1/2^i = 5^i.
        // 5^23 is not an exact 64-bit floating point number,
        // so have to use bc for the math.
+       // Go up to 60 to be large enough for 32bit and 64bit platforms.
        /*
-               seq 27 | sed 's/^/5^/' | bc |
-               awk 'BEGIN{ print "\tleftCheat{ 0, \"\" }," }
+               seq 60 | sed 's/^/5^/' | bc |
+               awk 'BEGIN{ print "\t{ 0, \"\" }," }
                {
                        log2 = log(2)/log(10)
-                       printf("\tleftCheat{ %d, \"%s\" },\t// * %d\n",
+                       printf("\t{ %d, \"%s\" },\t// * %d\n",
                                int(log2*NR+1), $0, 2**NR)
                }'
        */
        {0, ""},
-       {1, "5"},                   // * 2
-       {1, "25"},                  // * 4
-       {1, "125"},                 // * 8
-       {2, "625"},                 // * 16
-       {2, "3125"},                // * 32
-       {2, "15625"},               // * 64
-       {3, "78125"},               // * 128
-       {3, "390625"},              // * 256
-       {3, "1953125"},             // * 512
-       {4, "9765625"},             // * 1024
-       {4, "48828125"},            // * 2048
-       {4, "244140625"},           // * 4096
-       {4, "1220703125"},          // * 8192
-       {5, "6103515625"},          // * 16384
-       {5, "30517578125"},         // * 32768
-       {5, "152587890625"},        // * 65536
-       {6, "762939453125"},        // * 131072
-       {6, "3814697265625"},       // * 262144
-       {6, "19073486328125"},      // * 524288
-       {7, "95367431640625"},      // * 1048576
-       {7, "476837158203125"},     // * 2097152
-       {7, "2384185791015625"},    // * 4194304
-       {7, "11920928955078125"},   // * 8388608
-       {8, "59604644775390625"},   // * 16777216
-       {8, "298023223876953125"},  // * 33554432
-       {8, "1490116119384765625"}, // * 67108864
-       {9, "7450580596923828125"}, // * 134217728
+       {1, "5"},                                           // * 2
+       {1, "25"},                                          // * 4
+       {1, "125"},                                         // * 8
+       {2, "625"},                                         // * 16
+       {2, "3125"},                                        // * 32
+       {2, "15625"},                                       // * 64
+       {3, "78125"},                                       // * 128
+       {3, "390625"},                                      // * 256
+       {3, "1953125"},                                     // * 512
+       {4, "9765625"},                                     // * 1024
+       {4, "48828125"},                                    // * 2048
+       {4, "244140625"},                                   // * 4096
+       {4, "1220703125"},                                  // * 8192
+       {5, "6103515625"},                                  // * 16384
+       {5, "30517578125"},                                 // * 32768
+       {5, "152587890625"},                                // * 65536
+       {6, "762939453125"},                                // * 131072
+       {6, "3814697265625"},                               // * 262144
+       {6, "19073486328125"},                              // * 524288
+       {7, "95367431640625"},                              // * 1048576
+       {7, "476837158203125"},                             // * 2097152
+       {7, "2384185791015625"},                            // * 4194304
+       {7, "11920928955078125"},                           // * 8388608
+       {8, "59604644775390625"},                           // * 16777216
+       {8, "298023223876953125"},                          // * 33554432
+       {8, "1490116119384765625"},                         // * 67108864
+       {9, "7450580596923828125"},                         // * 134217728
+       {9, "37252902984619140625"},                        // * 268435456
+       {9, "186264514923095703125"},                       // * 536870912
+       {10, "931322574615478515625"},                      // * 1073741824
+       {10, "4656612873077392578125"},                     // * 2147483648
+       {10, "23283064365386962890625"},                    // * 4294967296
+       {10, "116415321826934814453125"},                   // * 8589934592
+       {11, "582076609134674072265625"},                   // * 17179869184
+       {11, "2910383045673370361328125"},                  // * 34359738368
+       {11, "14551915228366851806640625"},                 // * 68719476736
+       {12, "72759576141834259033203125"},                 // * 137438953472
+       {12, "363797880709171295166015625"},                // * 274877906944
+       {12, "1818989403545856475830078125"},               // * 549755813888
+       {13, "9094947017729282379150390625"},               // * 1099511627776
+       {13, "45474735088646411895751953125"},              // * 2199023255552
+       {13, "227373675443232059478759765625"},             // * 4398046511104
+       {13, "1136868377216160297393798828125"},            // * 8796093022208
+       {14, "5684341886080801486968994140625"},            // * 17592186044416
+       {14, "28421709430404007434844970703125"},           // * 35184372088832
+       {14, "142108547152020037174224853515625"},          // * 70368744177664
+       {15, "710542735760100185871124267578125"},          // * 140737488355328
+       {15, "3552713678800500929355621337890625"},         // * 281474976710656
+       {15, "17763568394002504646778106689453125"},        // * 562949953421312
+       {16, "88817841970012523233890533447265625"},        // * 1125899906842624
+       {16, "444089209850062616169452667236328125"},       // * 2251799813685248
+       {16, "2220446049250313080847263336181640625"},      // * 4503599627370496
+       {16, "11102230246251565404236316680908203125"},     // * 9007199254740992
+       {17, "55511151231257827021181583404541015625"},     // * 18014398509481984
+       {17, "277555756156289135105907917022705078125"},    // * 36028797018963968
+       {17, "1387778780781445675529539585113525390625"},   // * 72057594037927936
+       {18, "6938893903907228377647697925567626953125"},   // * 144115188075855872
+       {18, "34694469519536141888238489627838134765625"},  // * 288230376151711744
+       {18, "173472347597680709441192448139190673828125"}, // * 576460752303423488
+       {19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976
 }
 
 // Is the leading prefix of b lexicographically less than s?
@@ -237,11 +272,11 @@ func leftShift(a *decimal, k uint) {
 
        r := a.nd         // read index
        w := a.nd + delta // write index
-       n := 0
 
        // Pick up a digit, put down a digit.
+       var n uint
        for r--; r >= 0; r-- {
-               n += (int(a.d[r]) - '0') << k
+               n += (uint(a.d[r]) - '0') << k
                quo := n / 10
                rem := n - 10*quo
                w--