Commit c832ecf0 authored by Rob Pike's avatar Rob Pike

gob: avoid one copy for every message written.

Plus the need for a second in-memory buffer.
Plays a bit fast and loose with the contents of a byte buffer,
but saves a potentially huge allocation. The gotest
run is about 10% faster overall after this change.

R=golang-dev, r, gri
CC=golang-dev
https://golang.org/cl/5236043
parent c9dd2e41
...@@ -453,6 +453,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { ...@@ -453,6 +453,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
// should be written to b, before the encoded value. // should be written to b, before the encoded value.
enc.pushWriter(b) enc.pushWriter(b)
data := new(bytes.Buffer) data := new(bytes.Buffer)
data.Write(spaceForLength)
enc.encode(data, iv.Elem(), ut) enc.encode(data, iv.Elem(), ut)
if enc.err != nil { if enc.err != nil {
error(enc.err) error(enc.err)
......
...@@ -20,11 +20,16 @@ type Encoder struct { ...@@ -20,11 +20,16 @@ type Encoder struct {
sent map[reflect.Type]typeId // which types we've already sent sent map[reflect.Type]typeId // which types we've already sent
countState *encoderState // stage for writing counts countState *encoderState // stage for writing counts
freeList *encoderState // list of free encoderStates; avoids reallocation freeList *encoderState // list of free encoderStates; avoids reallocation
buf []byte // for collecting the output.
byteBuf bytes.Buffer // buffer for top-level encoderState byteBuf bytes.Buffer // buffer for top-level encoderState
err os.Error err os.Error
} }
// Before we encode a message, we reserve space at the head of the
// buffer in which to encode its length. This means we can use the
// buffer to assemble the message without another allocation.
const maxLength = 9 // Maximum size of an encoded length.
var spaceForLength = make([]byte, maxLength)
// NewEncoder returns a new encoder that will transmit on the io.Writer. // NewEncoder returns a new encoder that will transmit on the io.Writer.
func NewEncoder(w io.Writer) *Encoder { func NewEncoder(w io.Writer) *Encoder {
enc := new(Encoder) enc := new(Encoder)
...@@ -61,20 +66,22 @@ func (enc *Encoder) setError(err os.Error) { ...@@ -61,20 +66,22 @@ func (enc *Encoder) setError(err os.Error) {
// writeMessage sends the data item preceded by a unsigned count of its length. // writeMessage sends the data item preceded by a unsigned count of its length.
func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) { func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) {
enc.countState.encodeUint(uint64(b.Len())) // Space has been reserved for the length at the head of the message.
// Build the buffer. // This is a little dirty: we grab the slice from the bytes.Buffer and massage
countLen := enc.countState.b.Len() // it by hand.
total := countLen + b.Len() message := b.Bytes()
if total > len(enc.buf) { messageLen := len(message) - maxLength
enc.buf = make([]byte, total+1000) // extra for growth // Encode the length.
} enc.countState.b.Reset()
// Place the length before the data. enc.countState.encodeUint(uint64(messageLen))
// TODO(r): avoid the extra copy here. // Copy the length to be a prefix of the message.
enc.countState.b.Read(enc.buf[0:countLen]) offset := maxLength - enc.countState.b.Len()
// Now the data. copy(message[offset:], enc.countState.b.Bytes())
b.Read(enc.buf[countLen:total])
// Write the data. // Write the data.
_, err := w.Write(enc.buf[0:total]) _, err := w.Write(message[offset:])
// Drain the buffer and restore the space at the front for the count of the next message.
b.Reset()
b.Write(spaceForLength)
if err != nil { if err != nil {
enc.setError(err) enc.setError(err)
} }
...@@ -224,6 +231,7 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error { ...@@ -224,6 +231,7 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
enc.err = nil enc.err = nil
enc.byteBuf.Reset() enc.byteBuf.Reset()
enc.byteBuf.Write(spaceForLength)
state := enc.newEncoderState(&enc.byteBuf) state := enc.newEncoderState(&enc.byteBuf)
enc.sendTypeDescriptor(enc.writer(), state, ut) enc.sendTypeDescriptor(enc.writer(), state, ut)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment