diff --git a/bson/bson.go b/bson/bson.go index c023b0638..ca1420825 100644 --- a/bson/bson.go +++ b/bson/bson.go @@ -508,8 +508,15 @@ func handleErr(err *error) { // } // func Marshal(in interface{}) (out []byte, err error) { + return MarshalBuffer(in, make([]byte, 0, initialBufferSize)) +} + +// MarshalBuffer behaves the same way as Marshal, except that instead of +// allocating a new byte slice it tries to use the received byte slice and +// only allocates more memory if necessary to fit the marshaled value. +func MarshalBuffer(in interface{}, buf []byte) (out []byte, err error) { defer handleErr(&err) - e := &encoder{make([]byte, 0, initialBufferSize)} + e := &encoder{buf} e.addDoc(reflect.ValueOf(in)) return e.out, nil } diff --git a/bson/bson_test.go b/bson/bson_test.go index 7727f4327..d770e5132 100644 --- a/bson/bson_test.go +++ b/bson/bson_test.go @@ -246,6 +246,13 @@ func (s *S) TestUnmarshalNonNilInterface(c *C) { c.Assert(m, DeepEquals, bson.M{"a": 1}) } +func (s *S) TestMarshalBuffer(c *C) { + buf := make([]byte, 0, 256) + data, err := bson.MarshalBuffer(bson.M{"a": 1}, buf) + c.Assert(err, IsNil) + c.Assert(data, DeepEquals, buf[:len(data)]) +} + // -------------------------------------------------------------------------- // Some one way marshaling operations which would unmarshal differently. diff --git a/session_test.go b/session_test.go index ea7f55c03..38470fe95 100644 --- a/session_test.go +++ b/session_test.go @@ -35,6 +35,7 @@ import ( "sort" "strconv" "strings" + "testing" "time" mgo "github.com/domodwyer/mgo" @@ -4425,3 +4426,46 @@ func (s *S) BenchmarkFindIterRaw(c *C) { c.Assert(iter.Err(), IsNil) c.Assert(i, Equals, c.N) } + +func BenchmarkInsertSingle(b *testing.B) { + session, err := mgo.Dial("localhost:40001") + if err != nil { + b.Fatal(err) + } + defer session.Close() + + doc := bson.D{ + {"A", strings.Repeat("*", 256)}, + } + coll := session.DB("mydb").C("benchmarkcoll") + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := coll.Insert(doc) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkInsertMultiple(b *testing.B) { + session, err := mgo.Dial("localhost:40001") + if err != nil { + b.Fatal(err) + } + defer session.Close() + + docs := make([]interface{}, 100) + for i := range docs { + docs[i] = bson.D{ + {"A", strings.Repeat("*", 256)}, + } + } + coll := session.DB("mydb").C("benchmarkcoll") + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := coll.Insert(docs...) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/socket.go b/socket.go index 459b04da2..5c7ca22ec 100644 --- a/socket.go +++ b/socket.go @@ -375,13 +375,22 @@ func (socket *mongoSocket) SimpleQuery(op *queryOp) (data []byte, err error) { return data, err } +var bytesBufferPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, 256) + }, +} + func (socket *mongoSocket) Query(ops ...interface{}) (err error) { if lops := socket.flushLogout(); len(lops) > 0 { ops = append(lops, ops...) } - buf := make([]byte, 0, 256) + buf := bytesBufferPool.Get().([]byte) + defer func() { + bytesBufferPool.Put(buf[:0]) + }() // Serialize operations synchronously to avoid interrupting // other goroutines while we can't really be sending data. @@ -677,11 +686,11 @@ func addBSON(b []byte, doc interface{}) ([]byte, error) { if doc == nil { return append(b, 5, 0, 0, 0, 0), nil } - data, err := bson.Marshal(doc) + data, err := bson.MarshalBuffer(doc, b) if err != nil { return b, err } - return append(b, data...), nil + return data, nil } func setInt32(b []byte, pos int, i int32) {