From dfd0082b883452dd1d5aa2d4eede8011d61e6459 Mon Sep 17 00:00:00 2001 From: Ignacio Van Droogenbroeck Date: Mon, 2 Mar 2026 18:11:01 -0600 Subject: [PATCH] perf(encode): add MarshalAppend for zero-alloc buffer reuse Adds MarshalAppend(dst, v) that appends encoded bytes to a caller-provided buffer, avoiding the final make+copy in Marshal(). Refactors Marshal() to delegate to MarshalAppend(nil, v). Benchmark: StructMarshalAppend is ~26% faster and uses 94% less memory than Marshal when the caller reuses a buffer. --- bench_test.go | 15 +++++++++++++++ encode.go | 25 ++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/bench_test.go b/bench_test.go index 5e02f47..530fd11 100644 --- a/bench_test.go +++ b/bench_test.go @@ -304,6 +304,21 @@ func BenchmarkStructMarshal(b *testing.B) { } } +func BenchmarkStructMarshalAppend(b *testing.B) { + in := structForBenchmark() + buf := make([]byte, 0, 2048) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var err error + buf, err = msgpack.MarshalAppend(buf[:0], in) + if err != nil { + b.Fatal(err) + } + } +} + func BenchmarkStructUnmarshal(b *testing.B) { in := structForBenchmark() buf, err := msgpack.Marshal(in) diff --git a/encode.go b/encode.go index 8dec20b..dff5afd 100644 --- a/encode.go +++ b/encode.go @@ -63,23 +63,26 @@ func PutEncoder(enc *Encoder) { // Marshal returns the MessagePack encoding of v. func Marshal(v interface{}) ([]byte, error) { + return MarshalAppend(nil, v) +} + +// MarshalAppend is like Marshal but appends the encoded bytes to dst. +// This allows callers to reuse buffers and reduce allocations: +// +// buf = buf[:0] +// buf, err = msgpack.MarshalAppend(buf, v) +func MarshalAppend(dst []byte, v interface{}) ([]byte, error) { enc := GetEncoder() enc.resetForMarshal() - err := enc.Encode(v) - - var b []byte - if err == nil { - b = make([]byte, len(enc.wbuf)) - copy(b, enc.wbuf) + if err := enc.Encode(v); err != nil { + PutEncoder(enc) + return dst, err } + dst = append(dst, enc.wbuf...) PutEncoder(enc) - - if err != nil { - return nil, err - } - return b, nil + return dst, nil } type Encoder struct {