Skip to content

Commit d9af486

Browse files
committed
Reduce memory footprint by reusing buffers
This commit changes mongoSocket.Query to reuse allocated []byte buffers by getting and putting then into a sync.Pool. Also the function MarshalBuffer was added to the bson package and the addBSON function now uses it. 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 94191 90779 -3.62% BenchmarkInsertMultiple-4 1076423 937569 -12.90% benchmark old allocs new allocs delta BenchmarkInsertSingle-4 50 46 -8.00% BenchmarkInsertMultiple-4 456 242 -46.93% benchmark old bytes new bytes delta BenchmarkInsertSingle-4 2472 1320 -46.60% BenchmarkInsertMultiple-4 191180 29228 -84.71%
1 parent 699256f commit d9af486

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

bson/bson.go

+8-1
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

+7
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

+12-3
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,11 +683,11 @@ 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+
data, err := bson.MarshalBuffer(doc, b)
678687
if err != nil {
679688
return b, err
680689
}
681-
return append(b, data...), nil
690+
return data, nil
682691
}
683692

684693
func setInt32(b []byte, pos int, i int32) {

0 commit comments

Comments
 (0)