The *Encoder is almost always garbage. It doesn't need an
encodeState inside of it (and its bytes.Buffer), since it's
only needed locally inside of Encode.
benchmark old ns/op new ns/op delta
BenchmarkEncoderEncode 2562 2553 -0.35%
benchmark old bytes new bytes delta
BenchmarkEncoderEncode 283 102 -63.96%
R=r
CC=gobot, golang-dev
https://golang.org/cl/
9365044
scratch [64]byte
}
+// TODO(bradfitz): use a sync.Cache here
+var encodeStatePool = make(chan *encodeState, 8)
+
+func newEncodeState() *encodeState {
+ select {
+ case e := <-encodeStatePool:
+ e.Reset()
+ return e
+ default:
+ return new(encodeState)
+ }
+}
+
+func putEncodeState(e *encodeState) {
+ select {
+ case encodeStatePool <- e:
+ default:
+ }
+}
+
func (e *encodeState) marshal(v interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
if enc.err != nil {
return enc.err
}
- enc.e.Reset()
- err := enc.e.marshal(v)
+ e := newEncodeState()
+ err := e.marshal(v)
if err != nil {
return err
}
// is required if the encoded value was a number,
// so that the reader knows there aren't more
// digits coming.
- enc.e.WriteByte('\n')
+ e.WriteByte('\n')
- if _, err = enc.w.Write(enc.e.Bytes()); err != nil {
+ if _, err = enc.w.Write(e.Bytes()); err != nil {
enc.err = err
}
+ putEncodeState(e)
return err
}
w.Close()
}
}
+
+func BenchmarkEncoderEncode(b *testing.B) {
+ b.ReportAllocs()
+ type T struct {
+ X, Y string
+ }
+ v := &T{"foo", "bar"}
+ for i := 0; i < b.N; i++ {
+ if err := NewEncoder(ioutil.Discard).Encode(v); err != nil {
+ b.Fatal(err)
+ }
+ }
+}