aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/gob/decode.go
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2014-10-20 07:33:08 -0700
committerRob Pike <r@golang.org>2014-10-20 07:33:08 -0700
commit63acc48f8794aa51b91d7e482ba9271e54d3f77a (patch)
tree864f4723b923818b506b6ecb0db4f56b9a42f72a /src/encoding/gob/decode.go
parent8ba47e3d99b0cf1c86e10ece756ece3f9abeba04 (diff)
downloadgo-63acc48f8794aa51b91d7e482ba9271e54d3f77a.tar.gz
go-63acc48f8794aa51b91d7e482ba9271e54d3f77a.zip
encoding/gob: add custom decoder buffer for performance
As we did with encoding, provide a trivial byte reader for faster decoding. We can also reduce some of the copying by doing the allocation all at once using a slightly different interface from byte buffers. benchmark old ns/op new ns/op delta BenchmarkEndToEndPipe 13368 12902 -3.49% BenchmarkEndToEndByteBuffer 5969 5642 -5.48% BenchmarkEndToEndSliceByteBuffer 479485 470798 -1.81% BenchmarkEncodeComplex128Slice 92367 92201 -0.18% BenchmarkEncodeFloat64Slice 39990 38960 -2.58% BenchmarkEncodeInt32Slice 30510 27938 -8.43% BenchmarkEncodeStringSlice 33753 33365 -1.15% BenchmarkDecodeComplex128Slice 232278 196704 -15.32% BenchmarkDecodeFloat64Slice 150258 128191 -14.69% BenchmarkDecodeInt32Slice 133806 115748 -13.50% BenchmarkDecodeStringSlice 335117 300534 -10.32% LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/154360049
Diffstat (limited to 'src/encoding/gob/decode.go')
-rw-r--r--src/encoding/gob/decode.go63
1 files changed, 59 insertions, 4 deletions
diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go
index f44838e4cf..a5bef93141 100644
--- a/src/encoding/gob/decode.go
+++ b/src/encoding/gob/decode.go
@@ -7,7 +7,6 @@
package gob
import (
- "bytes"
"encoding"
"errors"
"io"
@@ -29,15 +28,71 @@ type decoderState struct {
dec *Decoder
// The buffer is stored with an extra indirection because it may be replaced
// if we load a type during decode (when reading an interface value).
- b *bytes.Buffer
+ b *decBuffer
fieldnum int // the last field number read.
buf []byte
next *decoderState // for free list
}
+// decBuffer is an extremely simple, fast implementation of a read-only byte buffer.
+// It is initialized by calling Size and then copying the data into the slice returned by Bytes().
+type decBuffer struct {
+ data []byte
+ offset int // Read offset.
+}
+
+func (d *decBuffer) Read(p []byte) (int, error) {
+ n := copy(p, d.data[d.offset:])
+ if n == 0 && len(p) != 0 {
+ return 0, io.EOF
+ }
+ d.offset += n
+ return n, nil
+}
+
+func (d *decBuffer) Drop(n int) {
+ if n > d.Len() {
+ panic("drop")
+ }
+ d.offset += n
+}
+
+// Size grows the buffer to exactly n bytes, so d.Bytes() will
+// return a slice of length n. Existing data is first discarded.
+func (d *decBuffer) Size(n int) {
+ d.Reset()
+ if cap(d.data) < n {
+ d.data = make([]byte, n)
+ } else {
+ d.data = d.data[0:n]
+ }
+}
+
+func (d *decBuffer) ReadByte() (byte, error) {
+ if d.offset >= len(d.data) {
+ return 0, io.EOF
+ }
+ c := d.data[d.offset]
+ d.offset++
+ return c, nil
+}
+
+func (d *decBuffer) Len() int {
+ return len(d.data) - d.offset
+}
+
+func (d *decBuffer) Bytes() []byte {
+ return d.data[d.offset:]
+}
+
+func (d *decBuffer) Reset() {
+ d.data = d.data[0:0]
+ d.offset = 0
+}
+
// We pass the bytes.Buffer separately for easier testing of the infrastructure
// without requiring a full Decoder.
-func (dec *Decoder) newDecoderState(buf *bytes.Buffer) *decoderState {
+func (dec *Decoder) newDecoderState(buf *decBuffer) *decoderState {
d := dec.freeList
if d == nil {
d = new(decoderState)
@@ -633,7 +688,7 @@ func (dec *Decoder) ignoreInterface(state *decoderState) {
error_(dec.err)
}
// At this point, the decoder buffer contains a delimited value. Just toss it.
- state.b.Next(int(state.decodeUint()))
+ state.b.Drop(int(state.decodeUint()))
}
// decodeGobDecoder decodes something implementing the GobDecoder interface.