mirror of
https://github.com/golang/go
synced 2024-11-25 08:57:58 -07:00
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
This commit is contained in:
parent
c9dd2e41b1
commit
c832ecf03e
@ -453,6 +453,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
|
||||
// should be written to b, before the encoded value.
|
||||
enc.pushWriter(b)
|
||||
data := new(bytes.Buffer)
|
||||
data.Write(spaceForLength)
|
||||
enc.encode(data, iv.Elem(), ut)
|
||||
if enc.err != nil {
|
||||
error(enc.err)
|
||||
|
@ -20,11 +20,16 @@ type Encoder struct {
|
||||
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.
|
||||
byteBuf bytes.Buffer // buffer for top-level encoderState
|
||||
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.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
enc := new(Encoder)
|
||||
@ -61,20 +66,22 @@ func (enc *Encoder) setError(err os.Error) {
|
||||
|
||||
// writeMessage sends the data item preceded by a unsigned count of its length.
|
||||
func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) {
|
||||
enc.countState.encodeUint(uint64(b.Len()))
|
||||
// Build the buffer.
|
||||
countLen := enc.countState.b.Len()
|
||||
total := countLen + b.Len()
|
||||
if total > len(enc.buf) {
|
||||
enc.buf = make([]byte, total+1000) // extra for growth
|
||||
}
|
||||
// Place the length before the data.
|
||||
// TODO(r): avoid the extra copy here.
|
||||
enc.countState.b.Read(enc.buf[0:countLen])
|
||||
// Now the data.
|
||||
b.Read(enc.buf[countLen:total])
|
||||
// Space has been reserved for the length at the head of the message.
|
||||
// This is a little dirty: we grab the slice from the bytes.Buffer and massage
|
||||
// it by hand.
|
||||
message := b.Bytes()
|
||||
messageLen := len(message) - maxLength
|
||||
// Encode the length.
|
||||
enc.countState.b.Reset()
|
||||
enc.countState.encodeUint(uint64(messageLen))
|
||||
// Copy the length to be a prefix of the message.
|
||||
offset := maxLength - enc.countState.b.Len()
|
||||
copy(message[offset:], enc.countState.b.Bytes())
|
||||
// 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 {
|
||||
enc.setError(err)
|
||||
}
|
||||
@ -224,6 +231,7 @@ func (enc *Encoder) EncodeValue(value reflect.Value) os.Error {
|
||||
|
||||
enc.err = nil
|
||||
enc.byteBuf.Reset()
|
||||
enc.byteBuf.Write(spaceForLength)
|
||||
state := enc.newEncoderState(&enc.byteBuf)
|
||||
|
||||
enc.sendTypeDescriptor(enc.writer(), state, ut)
|
||||
|
Loading…
Reference in New Issue
Block a user