]> Cypherpunks repositories - gostls13.git/commitdiff
strconv: replace small int string table with constant string
authorRobert Griesemer <gri@golang.org>
Fri, 17 Mar 2017 04:13:29 +0000 (21:13 -0700)
committerRobert Griesemer <gri@golang.org>
Fri, 17 Mar 2017 20:33:46 +0000 (20:33 +0000)
This reduces memory use yet still provides the significant
performance gain seen when using a fast path for small integers.

Improvement of this CL comparing to code without fast path:

name              old time/op  new time/op  delta
FormatIntSmall-8  35.6ns ± 1%   4.5ns ± 1%  -87.30%  (p=0.008 n=5+5)
AppendIntSmall-8  17.4ns ± 1%   9.4ns ± 3%  -45.70%  (p=0.008 n=5+5)

For comparison, here's the improvement before this CL to code without
fast path (1% better for FormatIntSmall):

name              old time/op  new time/op  delta
FormatIntSmall-8  35.6ns ± 1%   4.0ns ± 3%  -88.64%  (p=0.008 n=5+5)
AppendIntSmall-8  17.4ns ± 1%   8.2ns ± 1%  -52.80%  (p=0.008 n=5+5)

Thus, the code in this CL performs slower for small integers using fast
path then the prior version, but this is relative to an already very fast
version:

name              old time/op  new time/op  delta
FormatIntSmall-8  4.05ns ± 3%  4.52ns ± 1%  +11.81%  (p=0.008 n=5+5)
AppendIntSmall-8  8.21ns ± 1%  9.45ns ± 3%  +15.05%  (p=0.008 n=5+5)

Measured on 2.3 GHz Intel Core i7 running macOS Sierra 10.12.3.

Overall, it's still ~88% faster than without fast path for small integers,
so probably worth it as it removes 100 global string slices in favor of
a single string.

Credits: This is based on the original (but cleaned up) version of the
code by Aliaksandr Valialkin (https://go-review.googlesource.com/c/37963/).

Change-Id: Icda78679c8c14666d46257894e9fa3d7f35e58b8
Reviewed-on: https://go-review.googlesource.com/38319
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
src/strconv/itoa.go

index 2a21185a6b38e220fd0e0b41c9aef4b3720b8a62..76ca676c5fb072e6712751adec3b259e67046953 100644 (file)
@@ -4,12 +4,14 @@
 
 package strconv
 
+const fastSmalls = true // enable fast path for small integers
+
 // FormatUint returns the string representation of i in the given base,
 // for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
 // for digit values >= 10.
 func FormatUint(i uint64, base int) string {
-       if i < uint64(len(smallints)) && base == 10 {
-               return smallints[i]
+       if fastSmalls && i < nSmalls && base == 10 {
+               return small(int(i))
        }
        _, s := formatBits(nil, i, base, false, false)
        return s
@@ -19,8 +21,8 @@ func FormatUint(i uint64, base int) string {
 // for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
 // for digit values >= 10.
 func FormatInt(i int64, base int) string {
-       if 0 <= i && i < int64(len(smallints)) && base == 10 {
-               return smallints[i]
+       if fastSmalls && 0 <= i && i < nSmalls && base == 10 {
+               return small(int(i))
        }
        _, s := formatBits(nil, uint64(i), base, i < 0, false)
        return s
@@ -34,8 +36,8 @@ func Itoa(i int) string {
 // AppendInt appends the string form of the integer i,
 // as generated by FormatInt, to dst and returns the extended buffer.
 func AppendInt(dst []byte, i int64, base int) []byte {
-       if 0 <= i && i < int64(len(smallints)) && base == 10 {
-               return append(dst, smallints[i]...)
+       if fastSmalls && 0 <= i && i < nSmalls && base == 10 {
+               return append(dst, small(int(i))...)
        }
        dst, _ = formatBits(dst, uint64(i), base, i < 0, true)
        return dst
@@ -44,30 +46,39 @@ func AppendInt(dst []byte, i int64, base int) []byte {
 // AppendUint appends the string form of the unsigned integer i,
 // as generated by FormatUint, to dst and returns the extended buffer.
 func AppendUint(dst []byte, i uint64, base int) []byte {
-       if i < uint64(len(smallints)) && base == 10 {
-               return append(dst, smallints[i]...)
+       if fastSmalls && i < nSmalls && base == 10 {
+               return append(dst, small(int(i))...)
        }
        dst, _ = formatBits(dst, i, base, false, true)
        return dst
 }
 
+// small returns the string for an i with 0 <= i < nSmalls.
+func small(i int) string {
+       off := 0
+       if i < 10 {
+               off = 1
+       }
+       return smallsString[i*2+off : i*2+2]
+}
+
+const nSmalls = 100
+
+const smallsString = "00010203040506070809" +
+       "10111213141516171819" +
+       "20212223242526272829" +
+       "30313233343536373839" +
+       "40414243444546474849" +
+       "50515253545556575859" +
+       "60616263646566676869" +
+       "70717273747576777879" +
+       "80818283848586878889" +
+       "90919293949596979899"
+
 const host32bit = ^uint(0)>>32 == 0
 
 const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
 
-var smallints = [...]string{
-       "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-       "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
-       "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
-       "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
-       "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
-       "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
-       "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
-       "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
-       "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
-       "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
-}
-
 var shifts = [len(digits) + 1]uint{
        1 << 1: 1,
        1 << 2: 2,