]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/binary: speed up writing slices of integers
authorRob Pike <r@golang.org>
Fri, 9 Aug 2013 13:15:08 +0000 (23:15 +1000)
committerRob Pike <r@golang.org>
Fri, 9 Aug 2013 13:15:08 +0000 (23:15 +1000)
Simple approach. Still generates garbage, but not as much.

benchmark                        old ns/op    new ns/op    delta
BenchmarkWriteSlice1000Int32s        40260        18791  -53.33%

benchmark                         old MB/s     new MB/s  speedup
BenchmarkWriteSlice1000Int32s        99.35       212.87    2.14x

Fixes #2634.

R=golang-dev, crawshaw
CC=golang-dev
https://golang.org/cl/12680046

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

index 87bc3947ed9d97fbe9ff106c7f37bf242ae9dea9..28fc09480ee6dc1ebb65d01b6b167e18bdd8c138 100644 (file)
@@ -202,48 +202,85 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
        case int8:
                bs = b[:1]
                b[0] = byte(v)
+       case []int8:
+               bs = make([]byte, len(v))
+               for i, x := range v {
+                       bs[i] = byte(x)
+               }
        case *uint8:
                bs = b[:1]
                b[0] = *v
        case uint8:
                bs = b[:1]
                b[0] = byte(v)
+       case []uint8:
+               bs = v
        case *int16:
                bs = b[:2]
                order.PutUint16(bs, uint16(*v))
        case int16:
                bs = b[:2]
                order.PutUint16(bs, uint16(v))
+       case []int16:
+               bs = make([]byte, 2*len(v))
+               for i, x := range v {
+                       order.PutUint16(bs[2*i:], uint16(x))
+               }
        case *uint16:
                bs = b[:2]
                order.PutUint16(bs, *v)
        case uint16:
                bs = b[:2]
                order.PutUint16(bs, v)
+       case []uint16:
+               bs = make([]byte, 2*len(v))
+               for i, x := range v {
+                       order.PutUint16(bs[2*i:], x)
+               }
        case *int32:
                bs = b[:4]
                order.PutUint32(bs, uint32(*v))
        case int32:
                bs = b[:4]
                order.PutUint32(bs, uint32(v))
+       case []int32:
+               bs = make([]byte, 4*len(v))
+               for i, x := range v {
+                       order.PutUint32(bs[4*i:], uint32(x))
+               }
        case *uint32:
                bs = b[:4]
                order.PutUint32(bs, *v)
        case uint32:
                bs = b[:4]
                order.PutUint32(bs, v)
+       case []uint32:
+               bs = make([]byte, 4*len(v))
+               for i, x := range v {
+                       order.PutUint32(bs[4*i:], x)
+               }
        case *int64:
                bs = b[:8]
                order.PutUint64(bs, uint64(*v))
        case int64:
                bs = b[:8]
                order.PutUint64(bs, uint64(v))
+       case []int64:
+               bs = make([]byte, 8*len(v))
+               for i, x := range v {
+                       order.PutUint64(bs[8*i:], uint64(x))
+               }
        case *uint64:
                bs = b[:8]
                order.PutUint64(bs, *v)
        case uint64:
                bs = b[:8]
                order.PutUint64(bs, v)
+       case []uint64:
+               bs = make([]byte, 8*len(v))
+               for i, x := range v {
+                       order.PutUint64(bs[8*i:], x)
+               }
        }
        if bs != nil {
                _, err := w.Write(bs)
index 056f0998f27b94c01232f370360c65b65eab3636..083488adad5a3e00d8d7bf890facda2f5d7dae7d 100644 (file)
@@ -141,6 +141,52 @@ func TestWriteSlice(t *testing.T) {
        checkResult(t, "WriteSlice", BigEndian, err, buf.Bytes(), src)
 }
 
+// Addresses of arrays are easier to manipulate with reflection than are slices.
+var intArrays = []interface{}{
+       &[100]int8{},
+       &[100]int16{},
+       &[100]int32{},
+       &[100]int64{},
+       &[100]uint8{},
+       &[100]uint16{},
+       &[100]uint32{},
+       &[100]uint64{},
+}
+
+func TestSliceRoundTrip(t *testing.T) {
+       buf := new(bytes.Buffer)
+       for _, array := range intArrays {
+               src := reflect.ValueOf(array).Elem()
+               unsigned := false
+               switch src.Index(0).Kind() {
+               case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+                       unsigned = true
+               }
+               for i := 0; i < src.Len(); i++ {
+                       if unsigned {
+                               src.Index(i).SetUint(uint64(i * 0x87654321))
+                       } else {
+                               src.Index(i).SetInt(int64(i * 0x87654321))
+                       }
+               }
+               buf.Reset()
+               srcSlice := src.Slice(0, src.Len())
+               err := Write(buf, BigEndian, srcSlice.Interface())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               dst := reflect.New(src.Type()).Elem()
+               dstSlice := dst.Slice(0, dst.Len())
+               err = Read(buf, BigEndian, dstSlice.Interface())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if !reflect.DeepEqual(src.Interface(), dst.Interface()) {
+                       t.Fatal(src)
+               }
+       }
+}
+
 func TestWriteT(t *testing.T) {
        buf := new(bytes.Buffer)
        ts := T{}
@@ -312,3 +358,16 @@ func BenchmarkWriteInts(b *testing.B) {
                b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30])
        }
 }
+
+func BenchmarkWriteSlice1000Int32s(b *testing.B) {
+       slice := make([]int32, 1000)
+       buf := new(bytes.Buffer)
+       var w io.Writer = buf
+       b.SetBytes(4 * 1000)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               buf.Reset()
+               Write(w, BigEndian, slice)
+       }
+       b.StopTimer()
+}