]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/binary: fast path for reading slices
authorRob Pike <r@golang.org>
Fri, 9 Aug 2013 22:40:32 +0000 (08:40 +1000)
committerRob Pike <r@golang.org>
Fri, 9 Aug 2013 22:40:32 +0000 (08:40 +1000)
Again, it still allocates but the code is simple.

benchmark                       old ns/op    new ns/op    delta
BenchmarkReadSlice1000Int32s        35580        11465  -67.78%

benchmark                        old MB/s     new MB/s  speedup
BenchmarkReadSlice1000Int32s       112.42       348.86    3.10x

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12694048

src/pkg/encoding/binary/binary.go

index 28fc09480ee6dc1ebb65d01b6b167e18bdd8c138..107b80b9fef94ad7964bd566c5b49ce2206a4915 100644 (file)
@@ -134,30 +134,67 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
 // blank (_) field names is skipped; i.e., blank field names
 // may be used for padding.
 func Read(r io.Reader, order ByteOrder, data interface{}) error {
-       // Fast path for basic types.
+       // Fast path for basic types and slices.
        if n := intDestSize(data); n != 0 {
                var b [8]byte
-               bs := b[:n]
+               var bs []byte
+               if n > len(b) {
+                       bs = make([]byte, n)
+               } else {
+                       bs = b[:n]
+               }
                if _, err := io.ReadFull(r, bs); err != nil {
                        return err
                }
-               switch v := data.(type) {
+               switch data := data.(type) {
                case *int8:
-                       *v = int8(b[0])
+                       *data = int8(b[0])
                case *uint8:
-                       *v = b[0]
+                       *data = b[0]
                case *int16:
-                       *v = int16(order.Uint16(bs))
+                       *data = int16(order.Uint16(bs))
                case *uint16:
-                       *v = order.Uint16(bs)
+                       *data = order.Uint16(bs)
                case *int32:
-                       *v = int32(order.Uint32(bs))
+                       *data = int32(order.Uint32(bs))
                case *uint32:
-                       *v = order.Uint32(bs)
+                       *data = order.Uint32(bs)
                case *int64:
-                       *v = int64(order.Uint64(bs))
+                       *data = int64(order.Uint64(bs))
                case *uint64:
-                       *v = order.Uint64(bs)
+                       *data = order.Uint64(bs)
+               case []int8:
+                       for i, x := range bs { // Easier to loop over the input for 8-bit cases.
+                               data[i] = int8(x)
+                       }
+               case []uint8:
+                       for i, x := range bs { // Easier to loop over the input for 8-bit cases.
+                               data[i] = x
+                       }
+               case []int16:
+                       for i := range data {
+                               data[i] = int16(order.Uint16(bs[2*i:]))
+                       }
+               case []uint16:
+                       for i := range data {
+                               data[i] = order.Uint16(bs[2*i:])
+                       }
+               case []int32:
+                       for i := range data {
+                               data[i] = int32(order.Uint32(bs[4*i:]))
+                       }
+               case []uint32:
+                       for i := range data {
+                               data[i] = order.Uint32(bs[4*i:])
+                       }
+               case []int64:
+                       for i := range data {
+                               data[i] = int64(order.Uint64(bs[8*i:]))
+                       }
+               case []uint64:
+                       for i := range data {
+                               data[i] = order.Uint64(bs[8*i:])
+                       }
                }
                return nil
        }
@@ -572,18 +609,33 @@ func (e *encoder) skip(v reflect.Value) {
        e.buf = e.buf[n:]
 }
 
-// intDestSize returns the size of the integer that ptrType points to,
-// or 0 if the type is not supported.
-func intDestSize(ptrType interface{}) int {
-       switch ptrType.(type) {
+// intDestSize returns the size of the data required to represent the data when encoded.
+func intDestSize(data interface{}) int {
+       switch data := data.(type) {
        case *int8, *uint8:
                return 1
+       case []int8:
+               return len(data)
+       case []uint8:
+               return len(data)
        case *int16, *uint16:
                return 2
+       case []int16:
+               return 2 * len(data)
+       case []uint16:
+               return 2 * len(data)
        case *int32, *uint32:
                return 4
+       case []int32:
+               return 4 * len(data)
+       case []uint32:
+               return 4 * len(data)
        case *int64, *uint64:
                return 8
+       case []int64:
+               return 8 * len(data)
+       case []uint64:
+               return 8 * len(data)
        }
        return 0
 }