]> Cypherpunks repositories - gostls13.git/commitdiff
gob: keep free lists of encoder and decoder states.
authorRob Pike <r@golang.org>
Wed, 16 Mar 2011 04:30:07 +0000 (21:30 -0700)
committerRob Pike <r@golang.org>
Wed, 16 Mar 2011 04:30:07 +0000 (21:30 -0700)
Avoids 3 mallocs in a round trip encoding/decoding a struct.

R=rsc, rsc1
CC=golang-dev
https://golang.org/cl/4278052

src/pkg/gob/codec_test.go
src/pkg/gob/decode.go
src/pkg/gob/decoder.go
src/pkg/gob/encode.go
src/pkg/gob/encoder.go

index 4562e19309dcc337c66bb5b225d4f5daaf265f0c..e4364e6fd7a4f501f9c9cd0155654755ad151c27 100644 (file)
@@ -50,7 +50,7 @@ func testError(t *testing.T) {
 func TestUintCodec(t *testing.T) {
        defer testError(t)
        b := new(bytes.Buffer)
-       encState := newEncoderState(nil, b)
+       encState := newEncoderState(b)
        for _, tt := range encodeT {
                b.Reset()
                encState.encodeUint(tt.x)
@@ -58,7 +58,7 @@ func TestUintCodec(t *testing.T) {
                        t.Errorf("encodeUint: %#x encode: expected % x got % x", tt.x, tt.b, b.Bytes())
                }
        }
-       decState := newDecodeState(nil, b)
+       decState := newDecodeState(b)
        for u := uint64(0); ; u = (u + 1) * 7 {
                b.Reset()
                encState.encodeUint(u)
@@ -75,9 +75,9 @@ func TestUintCodec(t *testing.T) {
 func verifyInt(i int64, t *testing.T) {
        defer testError(t)
        var b = new(bytes.Buffer)
-       encState := newEncoderState(nil, b)
+       encState := newEncoderState(b)
        encState.encodeInt(i)
-       decState := newDecodeState(nil, b)
+       decState := newDecodeState(b)
        decState.buf = make([]byte, 8)
        j := decState.decodeInt()
        if i != j {
@@ -111,9 +111,16 @@ var complexResult = []byte{0x07, 0xFE, 0x31, 0x40, 0xFE, 0x33, 0x40}
 // The result of encoding "hello" with field number 7
 var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'}
 
-func newencoderState(b *bytes.Buffer) *encoderState {
+func newDecodeState(buf *bytes.Buffer) *decoderState {
+       d := new(decoderState)
+       d.b = buf
+       d.buf = make([]byte, uint64Size)
+       return d
+}
+
+func newEncoderState(b *bytes.Buffer) *encoderState {
        b.Reset()
-       state := newEncoderState(nil, b)
+       state := &encoderState{enc: nil, b: b}
        state.fieldnum = -1
        return state
 }
@@ -127,7 +134,7 @@ func TestScalarEncInstructions(t *testing.T) {
        {
                data := struct{ a bool }{true}
                instr := &encInstr{encBool, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(boolResult, b.Bytes()) {
                        t.Errorf("bool enc instructions: expected % x got % x", boolResult, b.Bytes())
@@ -139,7 +146,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a int }{17}
                instr := &encInstr{encInt, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(signedResult, b.Bytes()) {
                        t.Errorf("int enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -151,7 +158,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a uint }{17}
                instr := &encInstr{encUint, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(unsignedResult, b.Bytes()) {
                        t.Errorf("uint enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -163,7 +170,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a int8 }{17}
                instr := &encInstr{encInt8, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(signedResult, b.Bytes()) {
                        t.Errorf("int8 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -175,7 +182,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a uint8 }{17}
                instr := &encInstr{encUint8, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(unsignedResult, b.Bytes()) {
                        t.Errorf("uint8 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -187,7 +194,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a int16 }{17}
                instr := &encInstr{encInt16, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(signedResult, b.Bytes()) {
                        t.Errorf("int16 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -199,7 +206,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a uint16 }{17}
                instr := &encInstr{encUint16, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(unsignedResult, b.Bytes()) {
                        t.Errorf("uint16 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -211,7 +218,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a int32 }{17}
                instr := &encInstr{encInt32, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(signedResult, b.Bytes()) {
                        t.Errorf("int32 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -223,7 +230,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a uint32 }{17}
                instr := &encInstr{encUint32, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(unsignedResult, b.Bytes()) {
                        t.Errorf("uint32 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -235,7 +242,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a int64 }{17}
                instr := &encInstr{encInt64, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(signedResult, b.Bytes()) {
                        t.Errorf("int64 enc instructions: expected % x got % x", signedResult, b.Bytes())
@@ -247,7 +254,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a uint64 }{17}
                instr := &encInstr{encUint64, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(unsignedResult, b.Bytes()) {
                        t.Errorf("uint64 enc instructions: expected % x got % x", unsignedResult, b.Bytes())
@@ -259,7 +266,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a float32 }{17}
                instr := &encInstr{encFloat32, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(floatResult, b.Bytes()) {
                        t.Errorf("float32 enc instructions: expected % x got % x", floatResult, b.Bytes())
@@ -271,7 +278,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a float64 }{17}
                instr := &encInstr{encFloat64, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(floatResult, b.Bytes()) {
                        t.Errorf("float64 enc instructions: expected % x got % x", floatResult, b.Bytes())
@@ -283,7 +290,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a []byte }{[]byte("hello")}
                instr := &encInstr{encUint8Array, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(bytesResult, b.Bytes()) {
                        t.Errorf("bytes enc instructions: expected % x got % x", bytesResult, b.Bytes())
@@ -295,7 +302,7 @@ func TestScalarEncInstructions(t *testing.T) {
                b.Reset()
                data := struct{ a string }{"hello"}
                instr := &encInstr{encString, 6, 0, 0}
-               state := newencoderState(b)
+               state := newEncoderState(b)
                instr.op(instr, state, unsafe.Pointer(&data))
                if !bytes.Equal(bytesResult, b.Bytes()) {
                        t.Errorf("string enc instructions: expected % x got % x", bytesResult, b.Bytes())
@@ -315,7 +322,7 @@ func execDec(typ string, instr *decInstr, state *decoderState, t *testing.T, p u
 
 func newDecodeStateFromData(data []byte) *decoderState {
        b := bytes.NewBuffer(data)
-       state := newDecodeState(nil, b)
+       state := newDecodeState(b)
        state.fieldnum = -1
        return state
 }
index c47fea1a70a5de75ee72fcdb7c4eebac60686c01..d00ef7cd6573811a453f1dba429c80d6a7d0c05c 100644 (file)
@@ -31,18 +31,29 @@ type decoderState struct {
        b        *bytes.Buffer
        fieldnum int // the last field number read.
        buf      []byte
+       next     *decoderState // for free list
 }
 
 // We pass the bytes.Buffer separately for easier testing of the infrastructure
 // without requiring a full Decoder.
-func newDecodeState(dec *Decoder, buf *bytes.Buffer) *decoderState {
-       d := new(decoderState)
-       d.dec = dec
+func (dec *Decoder) newDecoderState(buf *bytes.Buffer) *decoderState {
+       d := dec.freeList
+       if d == nil {
+               d = new(decoderState)
+               d.dec = dec
+       } else {
+               dec.freeList = d.next
+       }
        d.b = buf
        d.buf = make([]byte, uint64Size)
        return d
 }
 
+func (dec *Decoder) freeDecoderState(d *decoderState) {
+       d.next = dec.freeList
+       dec.freeList = d
+}
+
 func overflow(name string) os.ErrorString {
        return os.ErrorString(`value for "` + name + `" out of range`)
 }
@@ -445,7 +456,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr)
                indir = int(ut.decIndir)
        }
        p = allocate(ut.base, p, indir)
-       state := newDecodeState(dec, &dec.buf)
+       state := dec.newDecoderState(&dec.buf)
        state.fieldnum = singletonField
        basep := p
        delta := int(state.decodeUint())
@@ -458,6 +469,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr)
                ptr = decIndirect(ptr, instr.indir)
        }
        instr.op(instr, state, ptr)
+       dec.freeDecoderState(state)
        return nil
 }
 
@@ -468,7 +480,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, p uintptr)
 // from the user's value, not from the innards of an engine.
 func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) (err os.Error) {
        p = allocate(ut.base.(*reflect.StructType), p, indir)
-       state := newDecodeState(dec, &dec.buf)
+       state := dec.newDecoderState(&dec.buf)
        state.fieldnum = -1
        basep := p
        for state.b.Len() > 0 {
@@ -492,12 +504,13 @@ func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr,
                instr.op(instr, state, p)
                state.fieldnum = fieldnum
        }
+       dec.freeDecoderState(state)
        return nil
 }
 
 // ignoreStruct discards the data for a struct with no destination.
 func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) {
-       state := newDecodeState(dec, &dec.buf)
+       state := dec.newDecoderState(&dec.buf)
        state.fieldnum = -1
        for state.b.Len() > 0 {
                delta := int(state.decodeUint())
@@ -515,13 +528,14 @@ func (dec *Decoder) ignoreStruct(engine *decEngine) (err os.Error) {
                instr.op(instr, state, unsafe.Pointer(nil))
                state.fieldnum = fieldnum
        }
+       dec.freeDecoderState(state)
        return nil
 }
 
 // ignoreSingle discards the data for a top-level non-struct value with no
 // destination. It's used when calling Decode with a nil value.
 func (dec *Decoder) ignoreSingle(engine *decEngine) (err os.Error) {
-       state := newDecodeState(dec, &dec.buf)
+       state := dec.newDecoderState(&dec.buf)
        state.fieldnum = singletonField
        delta := int(state.decodeUint())
        if delta != 0 {
@@ -529,6 +543,7 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) (err os.Error) {
        }
        instr := &engine.instr[singletonField]
        instr.op(instr, state, unsafe.Pointer(nil))
+       dec.freeDecoderState(state)
        return nil
 }
 
index 0c5fbbd7eab0d5a2e95e1af612636ffbea190e04..5e5afb37b4b5d7a2ae2515f5abc952ab02585d14 100644 (file)
@@ -22,7 +22,7 @@ type Decoder struct {
        wireType     map[typeId]*wireType                    // map from remote ID to local description
        decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines
        ignorerCache map[typeId]**decEngine                  // ditto for ignored objects
-       countState   *decoderState                           // reads counts from wire
+       freeList     *decoderState                           // list of free decoderStates; avoids reallocation
        countBuf     []byte                                  // used for decoding integers while parsing messages
        tmp          []byte                                  // temporary storage for i/o; saves reallocating
        err          os.Error
index adaf958e783b93b89e07503e6e30d672860b21d7..4dbafdddeb7d66587e496dd91d86dc39eabf3d0b 100644 (file)
@@ -25,10 +25,26 @@ type encoderState struct {
        sendZero bool                 // encoding an array element or map key/value pair; send zero values
        fieldnum int                  // the last field number written.
        buf      [1 + uint64Size]byte // buffer used by the encoder; here to avoid allocation.
+       next     *encoderState        // for free list
 }
 
-func newEncoderState(enc *Encoder, b *bytes.Buffer) *encoderState {
-       return &encoderState{enc: enc, b: b}
+func (enc *Encoder) newEncoderState(b *bytes.Buffer) *encoderState {
+       e := enc.freeList
+       if e == nil {
+               e = new(encoderState)
+               e.enc = enc
+       } else {
+               enc.freeList = e.next
+       }
+       e.sendZero = false
+       e.fieldnum = 0
+       e.b = b
+       return e
+}
+
+func (enc *Encoder) freeEncoderState(e *encoderState) {
+       e.next = enc.freeList
+       enc.freeList = e
 }
 
 // Unsigned integers have a two-state encoding.  If the number is less
@@ -326,7 +342,7 @@ const singletonField = 0
 
 // encodeSingle encodes a single top-level non-struct value.
 func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) {
-       state := newEncoderState(enc, b)
+       state := enc.newEncoderState(b)
        state.fieldnum = singletonField
        // There is no surrounding struct to frame the transmission, so we must
        // generate data even if the item is zero.  To do this, set sendZero.
@@ -339,11 +355,12 @@ func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintp
                }
        }
        instr.op(instr, state, p)
+       enc.freeEncoderState(state)
 }
 
 // encodeStruct encodes a single struct value.
 func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) {
-       state := newEncoderState(enc, b)
+       state := enc.newEncoderState(b)
        state.fieldnum = -1
        for i := 0; i < len(engine.instr); i++ {
                instr := &engine.instr[i]
@@ -355,11 +372,12 @@ func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintp
                }
                instr.op(instr, state, p)
        }
+       enc.freeEncoderState(state)
 }
 
 // encodeArray encodes the array whose 0th element is at p.
 func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) {
-       state := newEncoderState(enc, b)
+       state := enc.newEncoderState(b)
        state.fieldnum = -1
        state.sendZero = true
        state.encodeUint(uint64(length))
@@ -375,6 +393,7 @@ func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid ui
                op(nil, state, unsafe.Pointer(elemp))
                p += uintptr(elemWid)
        }
+       enc.freeEncoderState(state)
 }
 
 // encodeReflectValue is a helper for maps. It encodes the value v.
@@ -392,7 +411,7 @@ func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir in
 // Because map internals are not exposed, we must use reflection rather than
 // addresses.
 func (enc *Encoder) encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elemOp encOp, keyIndir, elemIndir int) {
-       state := newEncoderState(enc, b)
+       state := enc.newEncoderState(b)
        state.fieldnum = -1
        state.sendZero = true
        keys := mv.Keys()
@@ -401,6 +420,7 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elem
                encodeReflectValue(state, key, keyOp, keyIndir)
                encodeReflectValue(state, mv.Elem(key), elemOp, elemIndir)
        }
+       enc.freeEncoderState(state)
 }
 
 // encodeInterface encodes the interface value iv.
@@ -409,7 +429,7 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv *reflect.MapValue, keyOp, elem
 // by the concrete value.  A nil value gets sent as the empty string for the name,
 // followed by no value.
 func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue) {
-       state := newEncoderState(enc, b)
+       state := enc.newEncoderState(b)
        state.fieldnum = -1
        state.sendZero = true
        if iv.IsNil() {
@@ -445,6 +465,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv *reflect.InterfaceValue)
        if enc.err != nil {
                error(err)
        }
+       enc.freeEncoderState(state)
 }
 
 // encGobEncoder encodes a value that implements the GobEncoder interface.
@@ -456,10 +477,11 @@ func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value, index int
        if err != nil {
                error(err)
        }
-       state := newEncoderState(enc, b)
+       state := enc.newEncoderState(b)
        state.fieldnum = -1
        state.encodeUint(uint64(len(data)))
        state.b.Write(data)
+       enc.freeEncoderState(state)
 }
 
 var encOpTable = [...]encOp{
index 228445ff814401f9f09db73bcce662d36455ebc8..7045e8e892b0fe64e6ad4416b3060e371b4d5b39 100644 (file)
@@ -19,6 +19,7 @@ type Encoder struct {
        w          []io.Writer             // where to send the data
        sent       map[reflect.Type]typeId // which types we've already sent
        countState *encoderState           // stage for writing counts
+       freeList   *encoderState           // list of free encoderStates; avoids reallocation
        buf        []byte                  // for collecting the output.
        err        os.Error
 }
@@ -28,7 +29,7 @@ func NewEncoder(w io.Writer) *Encoder {
        enc := new(Encoder)
        enc.w = []io.Writer{w}
        enc.sent = make(map[reflect.Type]typeId)
-       enc.countState = newEncoderState(enc, new(bytes.Buffer))
+       enc.countState = enc.newEncoderState(new(bytes.Buffer))
        return enc
 }
 
@@ -218,7 +219,7 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
        }
 
        enc.err = nil
-       state := newEncoderState(enc, new(bytes.Buffer))
+       state := enc.newEncoderState(new(bytes.Buffer))
 
        enc.sendTypeDescriptor(enc.writer(), state, ut)
        enc.sendTypeId(state, ut)
@@ -234,5 +235,6 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
                enc.writeMessage(enc.writer(), state.b)
        }
 
+       enc.freeEncoderState(state)
        return enc.err
 }