Skip to content

Commit cdb160e

Browse files
committed
Reduce memory footprint by reusing buffers
This commit changes mongoSocket.Query and addBSON to reuse allocated []byte buffers by getting and putting then into a sync.Pool. Also the function MarshalBuffer was added to the bson package. It behaves in the exact same way to Marshal but instead of allocating a []byte buffer it receives an already existing buffer. Benchmarks show a really nice decrease in memory footprint specially when large messages are being sent to MongoDB, as is the case of inserting multiple documents at once. $ benchcmp before.txt after.txt benchmark old ns/op new ns/op delta BenchmarkInsertSingle-4 101203 95119 -6.01% BenchmarkInsertMultiple-4 1131565 1066556 -5.75% benchmark old allocs new allocs delta BenchmarkInsertSingle-4 50 48 -4.00% BenchmarkInsertMultiple-4 456 343 -24.78% benchmark old bytes new bytes delta BenchmarkInsertSingle-4 2472 1385 -43.97% BenchmarkInsertMultiple-4 191180 32525 -82.99%
1 parent b20eaf4 commit cdb160e

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed

bson/bson.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,15 @@ func handleErr(err *error) {
489489
// }
490490
//
491491
func Marshal(in interface{}) (out []byte, err error) {
492+
return MarshalBuffer(in, make([]byte, 0, initialBufferSize))
493+
}
494+
495+
// MarshalBuffer behaves the same way as Marshal, except that instead of
496+
// allocating a new byte slice it tries to use the received byte slice and
497+
// only allocates more memory if necessary to fit the marshaled value.
498+
func MarshalBuffer(in interface{}, buf []byte) (out []byte, err error) {
492499
defer handleErr(&err)
493-
e := &encoder{make([]byte, 0, initialBufferSize)}
500+
e := &encoder{buf}
494501
e.addDoc(reflect.ValueOf(in))
495502
return e.out, nil
496503
}

bson/bson_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ func (s *S) TestUnmarshalNonNilInterface(c *C) {
249249
c.Assert(m, DeepEquals, bson.M{"a": 1})
250250
}
251251

252+
func (s *S) TestMarshalBuffer(c *C) {
253+
buf := make([]byte, 0, 256)
254+
data, err := bson.MarshalBuffer(bson.M{"a": 1}, buf)
255+
c.Assert(err, IsNil)
256+
c.Assert(data, DeepEquals, buf[:len(data)])
257+
}
258+
252259
// --------------------------------------------------------------------------
253260
// Some one way marshaling operations which would unmarshal differently.
254261

socket.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,13 +372,22 @@ func (socket *mongoSocket) SimpleQuery(op *queryOp) (data []byte, err error) {
372372
return data, err
373373
}
374374

375+
var bytesBufferPool = sync.Pool{
376+
New: func() interface{} {
377+
return make([]byte, 0, 256)
378+
},
379+
}
380+
375381
func (socket *mongoSocket) Query(ops ...interface{}) (err error) {
376382

377383
if lops := socket.flushLogout(); len(lops) > 0 {
378384
ops = append(lops, ops...)
379385
}
380386

381-
buf := make([]byte, 0, 256)
387+
buf := bytesBufferPool.Get().([]byte)
388+
defer func() {
389+
bytesBufferPool.Put(buf[:0])
390+
}()
382391

383392
// Serialize operations synchronously to avoid interrupting
384393
// other goroutines while we can't really be sending data.
@@ -674,7 +683,9 @@ func addBSON(b []byte, doc interface{}) ([]byte, error) {
674683
if doc == nil {
675684
return append(b, 5, 0, 0, 0, 0), nil
676685
}
677-
data, err := bson.Marshal(doc)
686+
buf := bytesBufferPool.Get().([]byte)
687+
data, err := bson.MarshalBuffer(doc, buf)
688+
defer bytesBufferPool.Put(data[:0])
678689
if err != nil {
679690
return b, err
680691
}

0 commit comments

Comments
 (0)