]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/binary: add a non-reflect fast path for Write
authorBrad Fitzpatrick <bradfitz@golang.org>
Fri, 27 May 2011 23:29:33 +0000 (16:29 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Fri, 27 May 2011 23:29:33 +0000 (16:29 -0700)
before/after:
binary.BenchmarkWrite   100000      18312 ns/op
binary.BenchmarkWrite   500000       4468 ns/op

R=rsc, gri
CC=golang-dev
https://golang.org/cl/4515154

src/pkg/encoding/binary/binary.go
src/pkg/encoding/binary/binary_test.go

index d0185ebb71ded0b66f6d83524f3286bd32b12dcf..8e55cb23b7c4707bf2a6ce6fd5fec4e02f76d788 100644 (file)
@@ -125,37 +125,18 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
 // Bytes read from r are decoded using the specified byte order
 // and written to successive fields of the data.
 func Read(r io.Reader, order ByteOrder, data interface{}) os.Error {
-       // Fast path for basic types
-       var n int
-       switch data.(type) {
-       case *int8:
-               n = 1
-       case *uint8:
-               n = 1
-       case *int16:
-               n = 2
-       case *uint16:
-               n = 2
-       case *int32:
-               n = 4
-       case *uint32:
-               n = 4
-       case *int64:
-               n = 8
-       case *uint64:
-               n = 8
-       }
-       if n != 0 {
-               var buf [8]byte
-               bs := buf[:n]
+       // Fast path for basic types.
+       if n := intDestSize(data); n != 0 {
+               var b [8]byte
+               bs := b[:n]
                if _, err := io.ReadFull(r, bs); err != nil {
                        return err
                }
                switch v := data.(type) {
                case *int8:
-                       *v = int8(buf[0])
+                       *v = int8(b[0])
                case *uint8:
-                       *v = buf[0]
+                       *v = b[0]
                case *int16:
                        *v = int16(order.Uint16(bs))
                case *uint16:
@@ -203,6 +184,63 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error {
 // Bytes written to w are encoded using the specified byte order
 // and read from successive fields of the data.
 func Write(w io.Writer, order ByteOrder, data interface{}) os.Error {
+       // Fast path for basic types.
+       var b [8]byte
+       var bs []byte
+       switch v := data.(type) {
+       case *int8:
+               bs = b[:1]
+               b[0] = byte(*v)
+       case int8:
+               bs = b[:1]
+               b[0] = byte(v)
+       case *uint8:
+               bs = b[:1]
+               b[0] = *v
+       case uint8:
+               bs = b[:1]
+               b[0] = byte(v)
+       case *int16:
+               bs = b[:2]
+               order.PutUint16(bs, uint16(*v))
+       case int16:
+               bs = b[:2]
+               order.PutUint16(bs, uint16(v))
+       case *uint16:
+               bs = b[:2]
+               order.PutUint16(bs, *v)
+       case uint16:
+               bs = b[:2]
+               order.PutUint16(bs, v)
+       case *int32:
+               bs = b[:4]
+               order.PutUint32(bs, uint32(*v))
+       case int32:
+               bs = b[:4]
+               order.PutUint32(bs, uint32(v))
+       case *uint32:
+               bs = b[:4]
+               order.PutUint32(bs, *v)
+       case uint32:
+               bs = b[:4]
+               order.PutUint32(bs, v)
+       case *int64:
+               bs = b[:8]
+               order.PutUint64(bs, uint64(*v))
+       case int64:
+               bs = b[:8]
+               order.PutUint64(bs, uint64(v))
+       case *uint64:
+               bs = b[:8]
+               order.PutUint64(bs, *v)
+       case uint64:
+               bs = b[:8]
+               order.PutUint64(bs, v)
+       }
+       if bs != nil {
+               _, err := w.Write(bs)
+               return err
+       }
        v := reflect.Indirect(reflect.ValueOf(data))
        size := TotalSize(v)
        if size < 0 {
@@ -442,3 +480,19 @@ func (e *encoder) value(v reflect.Value) {
                }
        }
 }
+
+// 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) {
+       case *int8, *uint8:
+               return 1
+       case *int16, *uint16:
+               return 2
+       case *int32, *uint32:
+               return 4
+       case *int64, *uint64:
+               return 8
+       }
+       return 0
+}
index e588b9be41cc36273fe730ef51d86d0ac7f32ec5..b266996f6355b07eea213fefaafc7a77f9e678c6 100644 (file)
@@ -201,3 +201,35 @@ func BenchmarkRead(b *testing.B) {
                panic("no match")
        }
 }
+
+func BenchmarkWrite(b *testing.B) {
+       buf := new(bytes.Buffer)
+       var w io.Writer = buf
+
+       for i := 0; i < b.N; i++ {
+               buf.Reset()
+               Write(w, BigEndian, &s.Int8)
+               Write(w, BigEndian, &s.Int16)
+               Write(w, BigEndian, &s.Int32)
+               Write(w, BigEndian, &s.Int64)
+               Write(w, BigEndian, &s.Uint8)
+               Write(w, BigEndian, &s.Uint16)
+               Write(w, BigEndian, &s.Uint32)
+               Write(w, BigEndian, &s.Uint64)
+               Write(w, BigEndian, s.Int8)
+               Write(w, BigEndian, s.Int16)
+               Write(w, BigEndian, s.Int32)
+               Write(w, BigEndian, s.Int64)
+               Write(w, BigEndian, s.Uint8)
+               Write(w, BigEndian, s.Uint16)
+               Write(w, BigEndian, s.Uint32)
+               Write(w, BigEndian, s.Uint64)
+       }
+
+       if !bytes.Equal(buf.Bytes()[:30], big[:30]) {
+               panic("first half doesn't match")
+       }
+       if !bytes.Equal(buf.Bytes()[30:], big[:30]) {
+               panic("second half doesn't match")
+       }
+}